Повторите попытку с предупреждением, используя реактивный какао / ввести побочный эффект перед повторной попыткой

#objective-c #reactive-cocoa

#objective-c #реактивный-cocoa

Вопрос:

Я только начал изучать Reactive Cocoa и подумал, что сценарий из многих приложений, с которыми я сейчас работаю, должен быть хорошей отправной точкой. При запуске приложения оно проверяет, находится ли пользователь в настоящее время в стране, занесенной в белый список, прежде чем разрешить пользователю войти в приложение. Итак, идеальная последовательность должна выглядеть примерно так:

  1. Проверьте местоположение устройства, используя местоположение ядра.
  2. Обратный код местоположения с помощью CLGeocoder.
  3. Сравните код страны с белым списком.
  4. Продолжайте загрузку приложения.

Если (1) не удается, я хочу сообщить пользователю, что службы определения местоположения должны быть включены с помощью a UIAlertView с помощью одной кнопки повтора. И именно здесь я, похоже, не могу понять, как это должно быть сделано. Текущий код ниже:

 @weakify( self )
[[[[[[self
    findLocation]
    doError:^( NSError *error ) {
        @strongify( self )
        // There was an error fetching the location so we ask
        // the user to enable location services.
        //
        [self askUserToEnableLocationServices];
    }]
    retry] // What do we really retry here? It doesn't seem like it's [self findLocation]
    flattenMap:^RACStream *( CLLocation *newLocation ) {
        @strongify( self )
        return [self reverseGeocodeLocation:newLocation];
    }]
    flattenMap:^RACStream *( NSString *ISOCountryCode ) {
        @strongify( self )
        return [self checkCountryPermissibleSignal:ISOCountryCode];
    }]
    subscribeNext:^( id x ) {
        NSLog( @"Country is valid!" );
    } error:^( NSError *error ) {
        NSLog( @"Error: %@", error );
    }];
  

У кого-нибудь есть какие-либо данные по этому поводу? Я предполагаю, что я ошибаюсь в том, как retry это работает. Я также подозреваю, что я немного наивен в отношении того, как должен выглядеть весь поток. Но я застрял на этом уже пару вечеров. Есть что-то, что еще не щелкнуло.

Ответ №1:

Просто заметил, что вы спросили об этом на SO. Я опубликовал ответ в репозитории GitHub, но для потомков я также опубликую свой ответ здесь:

 @weakify( self )
[[[[[[self
    findLocation]
    doError:^( NSError *error ) {
        @strongify( self )
        // There was an error fetching the location so we ask
        // the user to enable location services.
        //
        [self askUserToEnableLocationServices];
    }]
    retry] // What do we really retry here? It doesn't seem like it's [self findLocation]
  

-retry больше не вызывается [self findLocation] . Вместо этого он создает новую подписку на объект RACSignal, который был возвращен [self findLocation] . Исходная подписка удалена из-за доставки ошибки в соответствии с контрактом / семантикой того, как работают сигналы при отправке ошибки.

Обратите внимание, что код в вашем вызове -doError: будет выполняться одновременно с новой подпиской, что может быть не тем, что вы предполагали. Другими словами, пока вы показываете пользователю представление предупреждения, вы одновременно повторно подписываетесь на сигнал -findLocation , возвращенный до того, как у пользователя была возможность предпринять какие-либо действия.

Вы можете использовать -catchTo: , чтобы указать альтернативный сигнал, на который следует подписаться, если в исходном сигнале выдается ошибка:

 @weakify( self )
RACSignal *recover = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
    @strongify( self );
    [self askTheUserToEnableLocationServices]
    return nil;
}];

[[[[[self
    findLocation]
    catchTo:recover]
    flattenMap:^RACStream *( CLLocation *newLocation ) {
        @strongify( self )
        return [self reverseGeocodeLocation:newLocation];
    }]
    flattenMap:^RACStream *( NSString *ISOCountryCode ) {
        @strongify( self )
        return [self checkCountryPermissibleSignal:ISOCountryCode];
    }]
    subscribeNext:^( id x ) {
        NSLog( @"Country is valid!" );
    } error:^( NSError *error ) {
        NSLog( @"Error: %@", error );
    }];
  

Комментарии:

1. Во-первых: да, я тоже задавал вопрос на GitHub. Больше крючков, больше рыбы? С этого момента я буду придерживаться SO, поскольку, похоже, вы отслеживаете оба. Во-вторых: я понимаю, что я действительно задал неправильный вопрос. То, что я намеревался спросить, было больше о том, как мне следует повторить первый шаг последовательности, пока пользователь не изменит настройки, чтобы разрешить службы определения местоположения для приложения. Но ваш ответ действительно привел меня в правильном направлении и ответил на вопрос, поэтому я принимаю его. Теперь я застрял на другой проблеме с этим кодом. Я свяжу его, когда вопрос будет поднят на SO.