>查找设备的位置
>每当位置键更改时,提取:
>当前天气
>小时预测
>每日预测
所以顺序是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
}];
}
最后,在我的单身人士中,我设置了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做排序:但是没有真正能够得到这个设置我想要的.
我也试图找到一个干净的方式来将异步抓取的结果绑定到单身的属性,但遇到麻烦让它工作.我无法弄清如何“延伸”获取RACSignals(注意:这就是-doNext:想法来自于每一个).
任何帮助清除或资源将是非常好的.谢谢!
解决方法
>获取最新数据的网络请求
>有状态地存储和呈现数据
这是重要的,因为第一个问题是无国籍的,而第二个几乎完全是有状态的.例如,在GitHub for Mac中,我们使用OCTClient执行联网,然后将返回的用户数据存储在“持久状态管理器”单例中.
一旦你像这样打破它,我认为会更容易理解.您的州长可以与网络客户端进行交互以启动请求,然后州长可以订阅这些请求并应用副作用.
首先,我们让-fetch …方法无状态,通过重写它们来使用转换而不是副作用:
- (RACSignal *)fetchCurrentConditions {
// build URL
return [[self fetchJSONFromURL:url] map:^(NSDictionary *json) {
return [MTLJSONAdapter modelOfClass:[CurrentCondition class] fromJSONDictionary:json error:nil];
}];
}
然后,您可以使用这些无状态方法并在其中更适合地注入副作用:
- (RACSignal *)updateCurrentConditions {
return [[self.networkClient
// If this signal sends its result on a background thread,make sure
// `currentCondition` is thread-safe,or make sure to deliver it to
// a kNown thread.
fetchCurrentConditions]
doNext:^(CurrentCondition *condition) {
self.currentCondition = condition;
}];
}
而且,要更新所有这些,您可以使用merge:(如在您的示例中)与-flattenMap相结合:从位置值映射到新的工作信号:
[[[RACObserve(self,currentLocation)
ignore:nil]
flattenMap:^(CLLocation *newLocation) {
return [RACSignal merge:@[
[self updateCurrentConditions],[self updateDailyForecast],[self updateHourlyForecast],]];
}]
subscribeError:^(NSError *error) {
NSLog(@"%@",error);
}];
或者,要在currentLocation发生更改时自动取消飞行中更新,请将-flattenMap替换为-switchToLatest:
[[[[RACObserve(self,currentLocation)
ignore:nil]
map:^(CLLocation *newLocation) {
return [RACSignal merge:@[
[self updateCurrentConditions],]];
}]
switchToLatest]
subscribeError:^(NSError *error) {
NSLog(@"%@",error);
}];
(ReactiveCocoa/ReactiveCocoa#786原始答复).