Как мне отложить отправку сообщения до тех пор, пока сигнал не будет подписан с помощью -catchTo:?

#objective-c #reactive-cocoa #frp

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

Вопрос:

Хорошо, теперь, когда я знаю, как создать повторную попытку цикла, я застрял на другой проблеме. Я думаю, что знаю, почему, но, похоже, не могу понять, как. В приведенном ниже коде у меня есть последовательность событий, которые будут собирать местоположение. Измените геокодирование и, наконец, сравните его со списком разрешенных кодов стран. Моя текущая проблема заключается в том, что предупреждение с просьбой разрешить службы определения местоположения для приложения будет отображаться, даже если ошибка не была отправлена. Я думаю, это связано с тем, что вызов [self retryWithAlert] будет вызван сразу после настройки последовательности, и я не должен размещать код для создания и отображения предупреждения там, где он находится в данный момент. Но когда я пытаюсь обернуть это в [RACSignal create:] Кажется, я просто не могу понять это правильно. И без дальнейших церемоний, вот код:

Сначала сама последовательность

 _locationManager = [[CLLocationManager alloc] init];
[_locationManager setDesiredAccuracy:kCLLocationAccuracyKilometer];

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

[_locationManager startUpdatingLocation];
 

И вот код для [self retryWithAlert]

 - (RACSignal *)retryWithAlert
{
    UIAlertView *retryAlert =
        [[UIAlertView alloc] initWithTitle:@"Trouble"
                                   message:@"Make sure location services are enabled and that the app is allowed to use them."
                                  delegate:self
                         cancelButtonTitle:@"Retry"
                         otherButtonTitles:nil];
    [retryAlert show];

    return [[[retryAlert
        rac_buttonClickedSignal]
        flattenMap:^RACStream *( id value ) {
            [_locationManager startUpdatingLocation];
            return [_locationManager rac_fetchLocationSignal];
        }]
        catch:^RACSignal *( NSError *error ) {
            return [self retryWithAlert];
        }];
}
 

Ответ №1:

Вы действительно близки. Как вы заметили, проблема в том, что -retryWithAlert сообщение отправляется немедленно при выполнении этого кода. Это происходит потому, что отправка этого сообщения является частью кода, который в первую очередь создает цепочку сигналов. Вы хотите, чтобы это сообщение отправлялось только при срабатывании catchTo сигнала.

Я думаю, что вместо -retryWithAlert немедленной отправки вы хотите отложить это до тех catchTo пор, пока не будет подписан сигнал ‘d (в этот момент можно отобразить оповещение). Это проще всего сделать с -[RACSignal defer:] помощью , которая позволяет поместить отправку -retryWithAlert сообщения в блок, который не вызывается до тех пор, пока не произойдет подписка.

 @weakify( self )
[[[[[[_locationManager
    rac_fetchLocationSignal]
    catchTo:[RACSignal defer:^{   // <--- defer until subscription
        @strongify( self );
        return [self retryWithAlert];
    }]]
    flattenMap:^RACStream *( CLLocation *newLocation ) {
        @strongify( self )
        return [self reverseGeocodeLocation:newLocation];
    }]
    ... (etc)