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