使用ReactiveCocoa开发RSS阅读器

时间:2021-05-09 23:39:13

目前已经完成的功能有对RSS的解析和Atom解析,RSS内容本地数据库存储和读取,抓取中状态进度展示,标记阅读状态,标记全部已读等。这些功能里我对一些异步操作产生的数据采用了ReactiveCocoa来对数据流向进行了控制,下面我来说下如何运用RAC来进行的开发。

初始时读取本地存储首页列表数据,过滤无效数据,监听列表数据变化进行列表更新

使用ReactiveCocoa开发RSS阅读器

这里会用到RAC这个宏可以方便的来进行键值和信号的绑定,RACObserve这个宏方便的进行键值变化的监听处理。具体实现代码如下:

@weakify(self);

//首页列表数据赋值,过滤无效数据

RAC(self, feeds) = [[[SMDB shareInstance] selectAllFeeds] filter:^BOOL(NSMutableArray *feedsArray) {

if (feedsArray.count > 0) {

return YES;

} else {

return NO;

}

}];

//监听列表数据变化进行列表更新

[RACObserve(self, feeds) subscribeNext:^(id x) {

@strongify(self);

[self.tableView reloadData];

}];

//本地读取首页订阅源数据

- (RACSignal *)selectAllFeeds {

@weakify(self);

return [RACSignal createSignal:^RACDisposable *(id subscriber) {

@strongify(self);

FMDatabase *db = [FMDatabase databaseWithPath:self.feedDBPath];

if ([db open]) {

FMResultSet *rs = [db executeQuery:@"select * from feeds"];

NSUInteger count = 0;

NSMutableArray *feedsArray = [NSMutableArray array];

while ([rs next]) {

SMFeedModel *feedModel = [[SMFeedModel alloc] init];

feedModel.fid = [rs intForColumn:@"fid"];

feedModel.title = [rs stringForColumn:@"title"];

feedModel.link = [rs stringForColumn:@"link"];

feedModel.des = [rs stringForColumn:@"des"];

feedModel.copyright = [rs stringForColumn:@"copyright"];

feedModel.generator = [rs stringForColumn:@"generator"];

feedModel.imageUrl = [rs stringForColumn:@"imageurl"];

feedModel.feedUrl = [rs stringForColumn:@"feedurl"];

feedModel.unReadCount = [rs intForColumn:@"unread"];

[feedsArray addObject:feedModel];

count++;

}

[subscriber sendNext:feedsArray];

[subscriber sendCompleted];

[db close];

}

return nil;

}];

}

通过网络获取订阅源最新内容,获取后进行本地存储,转成显示用的model进行列表的显示

这里的异步操作比较多,而且为了尽快取得数据采用的是并行队列,需要准确的获取到每个源完成的状态,包括解析的完成,本地存储完成,全部获取完成等数据完成情况。具体使用RAC方式的代码如下:

//获取所有feeds以及完成处理

- (void)fetchAllFeeds {

[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;

self.tableView.tableHeaderView = self.tbHeaderView;

self.fetchingCount = 0; //统计抓取数量

@weakify(self);

[[[[[[SMNetManager shareInstance] fetchAllFeedWithModelArray:self.feeds] map:^id(NSNumber *value) {

@strongify(self);

NSUInteger index = [value integerValue];

self.feeds[index] = [SMNetManager shareInstance].feeds[index];

return self.feeds[index];

}] doCompleted:^{

//抓完所有的feeds

@strongify(self);

NSLog(@"fetch complete");

//完成置为默认状态

self.tbHeaderLabel.text = @"";

self.tableView.tableHeaderView = [[UIView alloc] init];

self.fetchingCount = 0;

[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;

}] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(SMFeedModel *feedModel) {

//抓完一个

@strongify(self);

//显示抓取状态

self.fetchingCount += 1;

self.tbHeaderLabel.text = [NSString stringWithFormat:@"正在获取%@...(%lu/%lu)",feedModel.title,(unsigned long)self.fetchingCount,(unsigned long)self.feeds.count];

[self.tableView reloadData];

}];

}

//网络获取以及解析本地存储

- (RACSignal *)fetchAllFeedWithModelArray:(NSMutableArray *)modelArray {

@weakify(self);

return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

@strongify(self);

//创建并行队列

dispatch_queue_t fetchFeedQueue = dispatch_queue_create("com.starming.fetchfeed.fetchfeed", DISPATCH_QUEUE_CONCURRENT);

dispatch_group_t group = dispatch_group_create();

self.feeds = modelArray;

for (int i = 0; i < modelArray.count; i++) {

dispatch_group_enter(group);

SMFeedModel *feedModel = modelArray[i];

dispatch_async(fetchFeedQueue, ^{

[self GET:feedModel.feedUrl parameters:nil progress:nil success:^(NSURLSessionTask *task, id responseObject) {

//解析feed

self.feeds[i] = [self.feedStore updateFeedModelWithData:responseObject preModel:feedModel];

//入库存储

SMDB *db = [[SMDB alloc] init];

[[db insertWithFeedModel:self.feeds[i]] subscribeNext:^(NSNumber *x) {

SMFeedModel *model = (SMFeedModel *)self.feeds[i];

model.fid = [x integerValue];

//插入本地数据库成功后开始sendNext

[subscriber sendNext:@(i)];

//通知单个完成

dispatch_group_leave(group);

}];

} failure:^(NSURLSessionTask *operation, NSError *error) {

NSLog(@"Error: %@", error);

dispatch_group_leave(group);

}];

});//end dispatch async

}//end for

//全完成后执行事件

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

[subscriber sendCompleted];

});

return nil;

}];

}

读取RSS列表,异步读取,主线程更新

使用ReactiveCocoa开发RSS阅读器

这里通过RAC能够很方便的进行主线程操作UI,非主线程操作数据这样的操作,具体实现如下:

//获取列表数据以及对应的操作

- (void)selectFeedItems {

RACScheduler *scheduler = [RACScheduler schedulerWithPriority:RACSchedulerPriorityHigh];

@weakify(self);

[[[[[SMDB shareInstance] selectFeedItemsWithPage:self.page fid:self.feedModel.fid]

subscribeOn:scheduler]

deliverOn:[RACScheduler mainThreadScheduler]]

subscribeNext:^(NSMutableArray *x) {

@strongify(self);

if (self.listData.count > 0) {

//进入时加载

[self.listData addObjectsFromArray:x];

} else {

//加载更多

self.listData = x;

}

//刷新

[self.tableView reloadData];

} error:^(NSError *error) {

//处理无数据的显示

[self.tableView.mj_footer endRefreshingWithNoMoreData];

} completed:^{

//加载完成后的处理

[self.tableView.mj_footer endRefreshing];

}];

self.page += 1;

}

//数据库获取信号

- (RACSignal *)selectFeedItemsWithPage:(NSUInteger)page fid:(NSUInteger)fid {

@weakify(self);

return [RACSignal createSignal:^RACDisposable *(id subscriber) {

@strongify(self);

FMDatabase *db = [FMDatabase databaseWithPath:self.feedDBPath];

if ([db open]) {

//分页获取

FMResultSet *rs = [db executeQuery:@"select * from feeditem where fid = ? and isread = ? order by iid desc limit ?, 20",@(fid), @(0), @(page * 20)];

NSUInteger count = 0;

NSMutableArray *feedItemsArray = [NSMutableArray array];

//设置返回Array里的Model

while ([rs next]) {

SMFeedItemModel *itemModel = [[SMFeedItemModel alloc] init];

itemModel.iid = [rs intForColumn:@"iid"];

itemModel.fid = [rs intForColumn:@"fid"];

itemModel.link = [rs stringForColumn:@"link"];

itemModel.title = [rs stringForColumn:@"title"];

itemModel.author = [rs stringForColumn:@"author"];

itemModel.category = [rs stringForColumn:@"category"];

itemModel.pubDate = [rs stringForColumn:@"pubDate"];

itemModel.des = [rs stringForColumn:@"des"];

itemModel.isRead = [rs intForColumn:@"isread"];

[feedItemsArray addObject:itemModel];

count++;

}

if (count > 0) {

[subscriber sendNext:feedItemsArray];

} else {

//获取出错处理

[subscriber sendError:nil];

}

[subscriber sendCompleted];

[db close];

}

return nil;

}];

}

使用ReactiveCocoa开发RSS阅读器

完整代码可以在这里看:https://github.com/ming1016/GCDFetchFeed

使用ReactiveCocoa开发RSS阅读器的更多相关文章

  1. android rss阅读器开发一点小技巧

    这几天一直在学习开发Rss阅读器,遇到一个很坑的问题,InputSource这里总是出错.弄了好久,终于让我找到一个解决方法----看代码: new Thread(){ @Override publi ...

  2. 记第一次开发安卓应用——IT之家RSS阅读器

    这个学期学校开了安卓的课程,因为自己一直学习wp的开发,一直用的是.net和Silverlight这一套,也着实没有太多时间投入安卓的方向去,因为想着毕业也不从事安卓的工作,所以也一直没有怎么研究.但 ...

  3. &lbrack;UWP&rsqb;涨姿势UWP源码——极简的RSS阅读器

    涨姿势UWP,一个开源的RSS阅读器,一个纯粹的项目,一个有道德的APP,一个脱离了低级趣味的作者,一些有益于人民的代码.骚年,还等什么,来涨点姿势吧! 该项目代码可能会引起部分人群的不适,敏感人群请 ...

  4. RSS阅读器&amp&semi;BT sync

    ①RSS阅读器? 答:RSS阅读器是一种软件或是说一个程序,这种软件可以*读取RSS和Atom两种规范格式的文档,且这种读取RSS和Atom文档的软件有多个版本,由不同的人或公司开发,有着不同的名字 ...

  5. RSS阅读器(一)——dom4j读取xml&lpar;opml&rpar;文件

    接触java不久,偶有收获,最近想做一个web版RSS阅读器来锻炼一下.手头有几个从不同版本的foxmail中导出的opml文件,大家应该都知道,opml文件就是xml格式的.那么就先从这里入手,练习 ...

  6. 网易新闻RSS阅读器

    首先需要分析网易RSS订阅中心的网页布局情况. 网易RSS订阅中心:http://www.163.com/rss/ 你会发现RSS文件由一个<channel>元素及其子元素组成,除了频道本 ...

  7. 为什么说Thunderbird是最好的桌面RSS阅读器

    也许现在再讨论RSS阅读器似乎已经过时了,毕竟随着社交网络服务的发展,通过一个带有大众评分能力的社交网络(比如reddit),相比RSS的固定订阅而言,也许你能更快地在你所关心的话题上更快地获得新的资 ...

  8. 基于JSP的RSS阅读器的设计与实现

    阅读器访问地址:http://easyrss.tk/,欢迎体验! 阅读导览 一.    概述  二.    设计的基本概念和原理 三.    设计方案 四.    主要源代码 五.    阅读器使用说 ...

  9. 一步一步学Silverlight 2系列(18):综合实例之RSS阅读器

    一步一步学Silverlight 2系列(18):综合实例之RSS阅读器   概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支 ...

随机推荐

  1. 通过几个Hello World感受&period;NET Core全新的开发体验

    2016年6月27日,这是一个特殊的日子,微软全新的.NET开发平台.NET Core的RTM版本正式发布.我个人将.NET Core的核心特性归结为三点,它们的首字母组成一个非常好记的简称——COM ...

  2. iconfont 图标字体

    iconfont 技术的主要是将图标转化为字体来减少应用体积.如需在项目中使用iconfont技术,图标矢量图一开始都应合并转化为字体库.   优点: 减小体积,字体文件比图片要小 图标保真缩放,解决 ...

  3. 关于viewpager 里嵌套 listview 同时实现翻页功能的&OpenCurlyDoubleQuote;java&period;lang&period;IllegalStateException&colon; The specified child&period;&period;&period;&quot&semi;异常处理

    这几天做项目用到了ViewPager,因为它可以实现左右划动多个页面的效果,然后 再每个页面里使用ListView,运行时总是出现”PagerAdapter java.lang.IllegalStat ...

  4. phpcms 标签解析

    phpcms 每个pc标签对应modules控制器下一个 tag.class类 比如 {pc:content action="position" posid="2&quo ...

  5. jsp页面间的传值

    很多的时候我们只是把我们需要的数据,查询出来,然后用request.setAttribute("" ,"" )方法保存这个数据集合.再在我们能跳转到的下一个js ...

  6. crm2011i创建nt类型字段

    using System;     using Microsoft.Xrm.Sdk;     using Microsoft.Xrm.Sdk.Messages;     using Microsoft ...

  7. 深入tornado中的IOStream

    IOStream对tornado的高效起了很大的作用,他封装了socket的非阻塞IO的读写操作.大体上可以这么说,当连接建立后,服务端与客户端的请求响应都是基于IOStream的,也就是说:IOSt ...

  8. 4&period;variables

    变量在python可以是字符也可以是数字.例如:   x = 2 price = 2.5 word = 'Hello'   变量名在等号左边,值在右边,一旦变量被指定,就可以在程序的其他地方使用它. ...

  9. PythonStudy——函数对象的案例

    # part1 # 加法运算 def add(n1, n2): return n1 + n2 def low(n1, n2): return n1 - n2 # 四则运算 def computed(n ...

  10. eclipse Maven Dependencies 黑色背景说明

    记录工作点点滴滴,大到系统设计,源码分析,小到IDE设置. 这里要说的是eclipse中Maven Dependencies 为什么有些jar用黑色背景,如下图所示: 网上很多人说jar包在本地仓库不 ...