像下面的代码一样,在flattenMap:之后添加一个deliverOn:操作:
[[[[[[self requestAccessToTwitterSignal] then:^RACSignal *{ @strongify(self) return self.searchText.rac_textSignal; }] filter:^BOOL(NSString *text) { @strongify(self) return [self isValidSearchText:text]; }] flattenMap:^RACStream *(NSString *text) { @strongify(self) return [self signalForSearchWithText:text]; }] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) { NSLog(@"%@", x); } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }]; ?
现在重新运行,输入一些内容,停在断点。你应该能看到subscribeNext:error:block中的代码现在实在主线程执行了:
这是真的吗?一个简单的操作,就把事件流切换到不同的线程了?真的是太棒了!
现在你就能安全地更新UI啦!
注意:如果你看一下RACScheduler类,就能发现还有很多选项,比如不同的线程优先级,或者在管道中添加延迟。
现在要展示那些微博了。
如果你打开RWSearchResultsViewController.h 就会发现已经有一个displayTweets:方法了,它会让右边的view controller根据提供的微博数组来展示内容。实现非常简单,就是一个标准的UITableView数据源。displayTweets:方法需要的唯一一个参数就是包含RWTweet实例的数组。RWTweet模型已经包含在初始工程里了。
subscibeNext:error:里收到的数据目前是在signalForSearchWithText:里由返回的JSON值转换得到的一个NSDictionary。所以你怎么确定字典里的内容呢?
看一下Twitter的API文档,那里有返回值的样例。NSDictionary和这个结构对应,所以你能找到一个叫“statuses”的键,它对应的值是一个包含微博的NSArray,每个条文也是NSDictionary实例。
RWTweet已经有一个类方法tweetWithStatus:,方法从NSDictionary中取得需要的数据。所以你需要的做的就是写一个for循环,遍历数组,为每条微博创建一个RWTweet实例。
但我们这次不这么做。还有更好的方法。
这篇文章是关于ReactiveCocoa和函数式编程。如果用函数式API来实现把数据从一个格式转换为另一个会优雅很多。你将会用到LinqToObjectiveC来完成这个任务。
关闭TwitterInstant workspace,然后在文本编辑中打开之前创建的Podfile。加入新的依赖:
platform :ios, ‘7.0‘ pod ‘ReactiveCocoa‘, ‘2.1.8‘ pod ‘LinqToObjectiveC‘, ‘2.0.0‘
在这个文件中打开终端,输入下面的命令:
pod update
能看到输出和下面的类似:
Analyzing dependencies Downloading dependencies Installing LinqToObjectiveC (2.0.0) Using ReactiveCocoa (2.1.8) Generating Pods project Integrating client project ?
再次打开workspace,检查新的pod是否和下图一样显示出来:
打开RWSearchFormViewController.m,添加下列引用:
#import "RWTweet.h" #import "NSArray+LinqExtensions.h"
NSArray+LinqExtensions.h头文件是LinqToObjectiveC里的,它为NSArray添加了许多方法,能让你用流式API来转换、排序、分组和过滤其中的数据。现在就来用一下
把viewDidLoad中的代码更新成下面这样的:
[[[[[[self requestAccessToTwitterSignal] then:^RACSignal *{ @strongify(self) return self.searchText.rac_textSignal; }] filter:^BOOL(NSString *text) { @strongify(self) return [self isValidSearchText:text]; }] flattenMap:^RACStream *(NSString *text) { @strongify(self) return [self signalForSearchWithText:text]; }] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDictionary *jsonSearchResult) { NSArray *statuses = jsonSearchResult[@"statuses"]; NSArray *tweets = [statuses linq_select:^id(id tweet) { return [RWTweet tweetWithStatus:tweet]; }]; [self.resultsViewController displayTweets:tweets]; } error:^(NSError *error) { NSLog(@"An error occurred: %@", error); }];