使用ReactiveCocoa开发RSS阅读器
目前已经完成的功能有对RSS的解析和Atom解析,RSS内容本地数据库存储和读取,抓取中状态进度展示,标记阅读状态,标记全部已读等。这些功能里我对一些异步操作产生的数据采用了ReactiveCocoa来对数据流向进行了控制,下面我来说下如何运用RAC来进行的开发。
初始时读取本地存储首页列表数据,过滤无效数据,监听列表数据变化进行列表更新
这里会用到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列表,异步读取,主线程更新
这里通过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;
}];
}
完整代码可以在这里看:https://github.com/ming1016/GCDFetchFeed
使用ReactiveCocoa开发RSS阅读器的更多相关文章
- android rss阅读器开发一点小技巧
这几天一直在学习开发Rss阅读器,遇到一个很坑的问题,InputSource这里总是出错.弄了好久,终于让我找到一个解决方法----看代码: new Thread(){ @Override publi ...
- 记第一次开发安卓应用——IT之家RSS阅读器
这个学期学校开了安卓的课程,因为自己一直学习wp的开发,一直用的是.net和Silverlight这一套,也着实没有太多时间投入安卓的方向去,因为想着毕业也不从事安卓的工作,所以也一直没有怎么研究.但 ...
- [UWP]涨姿势UWP源码——极简的RSS阅读器
涨姿势UWP,一个开源的RSS阅读器,一个纯粹的项目,一个有道德的APP,一个脱离了低级趣味的作者,一些有益于人民的代码.骚年,还等什么,来涨点姿势吧! 该项目代码可能会引起部分人群的不适,敏感人群请 ...
- RSS阅读器&BT sync
①RSS阅读器? 答:RSS阅读器是一种软件或是说一个程序,这种软件可以自由读取RSS和Atom两种规范格式的文档,且这种读取RSS和Atom文档的软件有多个版本,由不同的人或公司开发,有着不同的名字 ...
- RSS阅读器(一)——dom4j读取xml(opml)文件
接触java不久,偶有收获,最近想做一个web版RSS阅读器来锻炼一下.手头有几个从不同版本的foxmail中导出的opml文件,大家应该都知道,opml文件就是xml格式的.那么就先从这里入手,练习 ...
- 网易新闻RSS阅读器
首先需要分析网易RSS订阅中心的网页布局情况. 网易RSS订阅中心:http://www.163.com/rss/ 你会发现RSS文件由一个<channel>元素及其子元素组成,除了频道本 ...
- 为什么说Thunderbird是最好的桌面RSS阅读器
也许现在再讨论RSS阅读器似乎已经过时了,毕竟随着社交网络服务的发展,通过一个带有大众评分能力的社交网络(比如reddit),相比RSS的固定订阅而言,也许你能更快地在你所关心的话题上更快地获得新的资 ...
- 基于JSP的RSS阅读器的设计与实现
阅读器访问地址:http://easyrss.tk/,欢迎体验! 阅读导览 一. 概述 二. 设计的基本概念和原理 三. 设计方案 四. 主要源代码 五. 阅读器使用说 ...
- 一步一步学Silverlight 2系列(18):综合实例之RSS阅读器
一步一步学Silverlight 2系列(18):综合实例之RSS阅读器 概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支 ...
随机推荐
- 无序数组a,求a[i]-a[j]的最大值,且i<j
一道面试题:对于无序数组a,求a[i]-a[j]的最大值,其中i<j package test; import java.util.Arrays; public class FindMax { ...
- BootStrap入门教程 (四) :JQuery类库插件(模态窗口,滚动监控,标签效果,提示效果,“泡芙”效果,警告区域,折叠效果,旋转木马,输入提示)
上讲回顾:Bootstrap组件丰富同时具有良好可扩展性,能够很好地应用在生产环境.这些组件包括按钮(Button),导航(Navigation),缩略图( thumbnails),提醒(Alert) ...
- 第三百零五天 how can I 坚持
今天去E世界拿抽的奖品,白去了 一趟,结果已经被拿过去了,明天还得去另一个地拿,太折腾了,今天走了1万4千多步. 哎,徐斌啊,出去见个面,这个折腾啊,受不了,想干他.开个玩笑. 黄东月,为什么打字莫名 ...
- google proto buffer安装和简单示例
1.安装 下载google proto buff. 解压下载的包,并且阅读README.txt,根据里面的指引进行安装. $ ./configure $ make $ make check $ mak ...
- linux下编译lua
curl -R -O http://www.lua.org/ftp/lua-5.2.3.tar.gz 编译代码时,遇到如下错误 /usr/lib/libreadline.so: undefined r ...
- 【转】使用JavaScriptCore在JS和OC间通信
http://www.cocoachina.com/ios/20160623/16796.html iOS 开发中,我们时不时的需要加载一些 Web 页面,一些需求使用 Web 页面来实现可以更可控, ...
- HDU 5794 A Simple Chess (容斥+DP+Lucas)
A Simple Chess 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5794 Description There is a n×m board ...
- jQuery基础学习5——JavaScript方法获取页面中的元素
给网页中的所有<p>元素添加onclick事件 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN& ...
- c# 实现IComparable、IComparer接口、Comparer类的详解
在默认情况下,对象的Equals(object o)方法(基类Object提供),是比较两个对象变量是否引用同一对象.我们要必须我自己的对象,必须自己定义对象比较方式.IComparable和ICom ...
- 在VB中利用Nuget包使用SQLite数据库和Linq to SQLite
上午解决了在C#中利用Nuget包使用SQLite数据库和Linq to SQLite,但是最后生成的是C#的cs类文件,对于我这熟悉VB而对C#白痴的来说怎么能行呢? 于是下午接着研究,既然生成的是 ...