使用ReactiveCocoa开发RSS阅读器

来源:星光社的戴铭(@戴铭)   

链接:http://www.jianshu.com/p/7391ff7d343f


目前已经完成的功能有对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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值