使用ReactiveCocoa開發(fā)RSS閱讀器

目前已經完成的功能有對RSS的解析和Atom解析,RSS內容本地數(shù)據(jù)庫存儲和讀取,抓取中狀態(tài)進度展示,標記閱讀狀態(tài),標記全部已讀等。這些功能里我對一些異步操作產生的數(shù)據(jù)采用了ReactiveCocoa來對數(shù)據(jù)流向進行了控制,下面我來說下如何運用RAC來進行的開發(fā)。

初始時讀取本地存儲首頁列表數(shù)據(jù),過濾無效數(shù)據(jù),監(jiān)聽列表數(shù)據(jù)變化進行列表更新

截圖
截圖

這里會用到RAC這個宏可以方便的來進行鍵值和信號的綁定,RACObserve這個宏方便的進行鍵值變化的監(jiān)聽處理。具體實現(xiàn)代碼如下:

@weakify(self);
//首頁列表數(shù)據(jù)賦值,過濾無效數(shù)據(jù)
RAC(self, feeds) = [[[SMDB shareInstance] selectAllFeeds] filter:^BOOL(NSMutableArray *feedsArray) {
    if (feedsArray.count > 0) {
        return YES;
    } else {
        return NO;
    }
}];

//監(jiān)聽列表數(shù)據(jù)變化進行列表更新
[RACObserve(self, feeds) subscribeNext:^(id x) {
    @strongify(self);
    [self.tableView reloadData];
}];

//本地讀取首頁訂閱源數(shù)據(jù)
- (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進行列表的顯示

這里的異步操作比較多,而且為了盡快取得數(shù)據(jù)采用的是并行隊列,需要準確的獲取到每個源完成的狀態(tài),包括解析的完成,本地存儲完成,全部獲取完成等數(shù)據(jù)完成情況。具體使用RAC方式的代碼如下:

//獲取所有feeds以及完成處理
- (void)fetchAllFeeds {
    [UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
    self.tableView.tableHeaderView = self.tbHeaderView;
    self.fetchingCount = 0; //統(tǒng)計抓取數(shù)量
    @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");
        //完成置為默認狀態(tài)
        self.tbHeaderLabel.text = @"";
        self.tableView.tableHeaderView = [[UIView alloc] init];
        self.fetchingCount = 0;
        [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
    }] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(SMFeedModel *feedModel) {
        //抓完一個
        @strongify(self);
        //顯示抓取狀態(tài)
        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);
        //創(chuàng)建并行隊列
        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];
                        //插入本地數(shù)據(jù)庫成功后開始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
        //全完成后執(zhí)行事件
        dispatch_group_notify(group, dispatch_get_main_queue(), ^{
            [subscriber sendCompleted];
        });
        return nil;
    }];
}

讀取RSS列表,異步讀取,主線程更新

截圖
截圖

這里通過RAC能夠很方便的進行主線程操作UI,非主線程操作數(shù)據(jù)這樣的操作,具體實現(xiàn)如下:

//獲取列表數(shù)據(jù)以及對應的操作
- (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) {
        //處理無數(shù)據(jù)的顯示
        [self.tableView.mj_footer endRefreshingWithNoMoreData];
    } completed:^{
        //加載完成后的處理
        [self.tableView.mj_footer endRefreshing];
    }];
    self.page += 1;
}

//數(shù)據(jù)庫獲取信號
- (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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,021評論 4 61
  • 1、使用網址:http://idea.iteblog.com/key.php 2、參考社區(qū)破解方法http://i...
    Jarly_Sun閱讀 590評論 0 0
  • 車子很快就到了公司生產總部的大樓下面,到了外圍,只見上百名的工人站在那里,很多人都舉著牌子,上面寫著要求加工資...
    櫞棶鰣鯢閱讀 322評論 0 0
  • 文/鴻運 星星之火開始點燃 指縫間飄過了縷縷云煙 輕輕的吸一口,一聲感嘆 層層白霧籠罩了人間 有時候看不清每個人的...
    HONGYUNDANGTOU閱讀 298評論 20 14

友情鏈接更多精彩內容