Я создаю демонстрационное приложение и пытаюсь соответствовать 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
для извлечения JSON из URL.
- (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
(обратите внимание: что идея -doNext:
появилась для каждого из них).
Любая помощь, очищающая это или ресурсы, будет действительно велика. Спасибо!