10

我正在构建一个演示应用程序,并试图尽可能地符合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:但并没有真正按照我的意愿进行设置。

我还试图找到一种干净的方法来将异步获取的结果绑定到我的单例的属性,但是在让它工作时遇到了麻烦。我无法弄清楚如何“扩展”获取RACSignals (注意:这就是-doNext:每个人的想法的来源)。

任何帮助解决这个问题或资源都会非常棒。谢谢!

4

2 回答 2

12
于 2013-09-09T23:13:52.190 回答
2

这是一个非常复杂的问题,我认为您只需要一些指示就可以理顺它。

  1. 除了显式订阅该位置之外,您还可以尝试使用 RACCommand
  2. RAC您可以使用宏将信号绑定到属性RAC(self.currentWeather) = currentWeatherSignal;
  3. 本教程是一个很好的例子,说明如何以一种干净的方式实现网络获取http://vimeo.com/65637501
  4. 尽量保留您的业务逻辑信号,而不是在每次事件发生时都设置它们。视频教程展示了一种非常优雅的方式来做到这一点。

备注:您是否有意在位置更新回调中停止位置更新?您可能无法在以后的 iOS 版本中重新启动它。(这太疯狂了,我也因此而愤怒。

于 2013-09-09T22:09:50.450 回答