我正在构建一个演示应用程序,并试图尽可能地符合ReactiveCocoa 设计模式。这是该应用程序的功能:
- 查找设备的位置
- 每当位置键更改时,获取:
- 当前天气
- 每小时预报
- 每日预报
所以顺序是 1) 更新位置 2) 合并所有 3 个天气获取。我已经构建了一个WeatherManager
暴露天气对象、位置信息和手动更新方法的单例。这个单例符合CLLocationManagerDelegate
协议。位置代码非常基本,所以我将其省略。唯一真正感兴趣的是:
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
// omitting accuracy & cache checking
CLLocation *location = [locations lastObject];
self.currentLocation = location;
[self.locationManager stopUpdatingLocation];
}
获取天气条件都非常相似,因此我构建了一个方法来生成RACSignal
从 URL 获取 JSON 的方法。
- (RACSignal *)fetchJSONFromURL:(NSURL *)url {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSURLSessionDataTask *dataTask = [self.session dataTaskWithURL:url completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (! error) {
NSError *jsonError = nil;
id json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&jsonError];
if (! jsonError) {
[subscriber sendNext:json];
}
else {
[subscriber sendError:jsonError];
}
}
else {
[subscriber sendError:error];
}
[subscriber sendCompleted];
}];
[dataTask resume];
return [RACDisposable disposableWithBlock:^{
[dataTask cancel];
}];
}];
}
这有助于我保持我的方法干净整洁,所以现在我有 3 个简短的方法来构建 URL 并返回 RACSignal。这里的好处是我可以创建副作用来解析 JSON 并分配适当的属性(注意:我在这里使用Mantle)。
- (RACSignal *)fetchCurrentConditions {
// build URL
return [[self fetchJSONFromURL:url] doNext:^(NSDictionary *json) {
// simply converts JSON to a Mantle object
self.currentCondition = [MTLJSONAdapter modelOfClass:[CurrentCondition class] fromJSONDictionary:json error:nil];
}];
}
- (RACSignal *)fetchHourlyForecast {
// build URL
return [[self fetchJSONFromURL:url] doNext:^(NSDictionary *json) {
// more work
}];
}
- (RACSignal *)fetchDailyForecast {
// build URL
return [[self fetchJSONFromURL:url] doNext:^(NSDictionary *json) {
// more work
}];
}
最后,在-init
我的单身人士中,我在位置上设置了 RAC 观察者,因为每次位置发生变化时,我都想获取和更新天气。
[[RACObserve(self, currentLocation)
filter:^BOOL(CLLocation *newLocation) {
return newLocation != nil;
}] subscribeNext:^(CLLocation *newLocation) {
[[RACSignal merge:@[[self fetchCurrentConditions], [self fetchDailyForecast], [self fetchHourlyForecast]]] subscribeError:^(NSError *error) {
NSLog(@"%@",error.localizedDescription);
}];
}];
一切正常,但我担心我偏离了响应式方式来构建我的获取和属性分配。我尝试过进行测序,-then:
但并没有真正按照我的意愿进行设置。
我还试图找到一种干净的方法来将异步获取的结果绑定到我的单例的属性,但是在让它工作时遇到了麻烦。我无法弄清楚如何“扩展”获取RACSignal
s (注意:这就是-doNext:
每个人的想法的来源)。
任何帮助解决这个问题或资源都会非常棒。谢谢!