专栏首页戴铭的博客使用ReactiveCocoa开发RSS阅读器

使用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<RACSubscriber> 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<RACSubscriber> 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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • FreshRSS:开源免费的RSS阅读器

    FreshRSS是一个基于PHP开发的RSS阅读器/订阅器。UI算同类产品中比较好看的了。目前FreshRSS还在不断更新完善中,看这个版本发布的架势,感觉这个...

    砸漏
  • iOS ReactiveCocoa(RAC)学习详解

    概述:     ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的一个框架,有时间,自己也了解学习了一下这个框架的一...

    Mr.RisingSun
  • 使用RSS和Wordpress定制化阅读

    大多数程序员都有写博客或者阅读博客的好习惯,那么几乎每个人都收集了一堆RSS源,那么如何聚合就成了一个问题.

    屈定
  • ReactiveCocoa之学习篇

    rectinajh
  • ReactiveCocoa中潜在的内存泄漏及解决方案

    ReactiveCocoa是GitHub开源的一个函数响应式编程框架,目前在美团App中大量使用。用过它的人都知道很好用,也确实为我们的生活带来了很多便利,特别...

    美团技术团队
  • 利用开源Tiny Tiny RSS程序搭建自己的RSS订阅服务器阅读工具

    从互联网才开始的时候,我们可阅读的网站并不多,无非就那几个门户网站看看新闻,凭借脑袋完全可以记住网址上网的时候直接打开就可以。随着个人网站等各种内容的出现,即便...

    老蒋
  • ReactiveCocoa函数响应式编程-基础篇目录:一、了解函数响应式编程二、ReactiveCocoa简介三、ReactiveCocoa集成四、ReactiveCocoa信号理解五、Reactiv

    一直以来,都很想学学ReactiveCocoa这个神奇的技术,但是最后都由于各种原因搁置了。这次终于也认真的研究一番,把自己学习心得整理出来留个记录。 目录: ...

    梧雨北辰
  • ReactiveCocoa 中 RACSignal 是如何发送信号的

    ReactiveCocoa是一个(第一个?)将函数响应式编程范例带入Objective-C的开源库。ReactiveCocoa是由Josh Abernathy和...

    一缕殇流化隐半边冰霜
  • ReactiveCocoa(二)

    Scott_Mr
  • ReactiveCocoa(二)

    Scott_Mr
  • RSS的相关知识

    rss是RDF Site Summary 的缩写(RDF是Resource Description Framework的缩写 ),是指将网站摘要用xml语言描述...

    大江小浪
  • 如何用RSS订阅?

    我们常常会有订阅别人文章的需求,有更新的时候希望能有提醒的功能,RSS就是这样一个订阅的方式。

    机智的程序员小熊
  • RSS 订阅

    博客提供 RSS 订阅应该是标配,这样读者就可以通过一些聚合阅读工具订阅你的博客,时时查看是否有文章更新,而不必每次都跳转到博客上来查看。现在我们就来为博客添加...

    追梦人物
  • iOS函数响应式编程以及ReactiveCocoa的使用

    打算在项目中大面积使用RAC来开发,所以整理一些常用的实践范例和比较完整的api说明方便开发时随时查阅

    用户7451029
  • .Net开发者不容错过的技术类RSS订阅指南

    作为一个.Net开发者,在如今这个信息大爆炸时代,网络上.net开发方面的信息浩如烟海(获取信息的渠道很多,比如各种 APP、公众号、聚合信息网站、博客园...

    Enjoy233
  • .Net开发者不容错过的技术类RSS订阅指南

    作为一个.Net开发者,在如今这个信息大爆炸时代,网络上.net开发方面的信息浩如烟海(获取信息的渠道很多,比如各种 APP、公众号、聚合信息网站、博客园、In...

    Enjoy233
  • 初识rss订阅,搭建自己的公益rss阅读器

    蚁阅基于Docker部署,服务器环境要求:Linux, 1G 内存,Docker + 镜像加速器

    imzql
  • iOS开发常用之响应式框架

    GuangdongQi
  • 断舍离_如何用RS订阅一切

    我的一天,曾经从手机Zaker客户端开始,到十余个微信公众号,到微博,再在公司刷虎嗅、腾讯新闻、行业新闻,然后到知乎日报结束。所有的这些都只为了回答:今天发生什...

    huginn 中文网

扫码关注云+社区

领取腾讯云代金券