Сигнал ReactiveCocoa для выборки данных при наблюдении за состоянием аутентификации

Совершенно новый для ReactiveCocoa, я пытаюсь создать сигнал, который асинхронно выбирает некоторый ресурс из удаленного API, к которому клиент должен сначала пройти аутентификацию. Аутентификация обрабатывается сначала получением токена от API, а затем передачей его через некоторый пользовательский заголовок HTTP для каждого последующего запроса. Тем не менее, пользовательский заголовок может быть установлен после подписки на сигнал fetchResource, что в текущей ситуации приводит к неаутентифицированному запросу. Я полагаю, что мог бы на самом деле построить запрос в блоке subscribeNext self.authenticationStatus, таким образом гарантируя, что токен будет установлен, но как я мог тогда обработать расположение сигнала?

- (RACSignal *)fetchResource
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSURLRequest *request = [self.requestSerializer
                                 requestWithMethod:@"GET"
                                 URLString:[[NSURL URLWithString:@"resource" relativeToURL:self.baseURL] absoluteString]
                                 parameters:nil error:nil];
        NSURLSessionDataTask *task = [self dataTaskWithRequest:request
                                      completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
            if (error) {
                [subscriber sendError:error];
            } else {
                [subscriber sendNext:responseObject];
                [subscriber sendCompleted];
            }
        }];

        // Actually trigger the request only once the authentication token has been fetched.
        [[self.authenticationStatus ignore:@NO] subscribeNext:^(id _) {
            [task resume];
        }];

        return [RACDisposable disposableWithBlock:^{
            [task cancel];
        }];
    }];
}

1 ответ

- (RACSignal *)fetchTokenWithCredentials:(Credentials *)credentials
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        // Fetch the token and send it to `subscriber`.
        Token *t = ... ;
        [subscriber sendNext:t];

        return nil;

    }];
}

- (RACSignal *)fetchResourceWithToken:(Token *)token
{
    return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        // Use `token` to set the request header. Then fetch
        // the resource and send it to `subscriber`. Basically
        // this part is what you already have.
        Resource *r = ... ;
        [subscriber sendNext:r];

        return nil;

    }];
}

В вашем контроллере представления представьте диалоговое окно модальной аутентификации, если у вас нет действительного токена. Когда пользователь нажимает кнопку "Отправить", сделайте что-то вроде следующего:

- (IBAction)handleAuthenticationSubmit:(id)sender
{
    Credentials *c = ... ;
    RACSignal *resourceSignal = [[[self fetchTokenWithCredentials:c]
            flattenMap:^(Token *t) {

                return [self fetchResourceWithToken:t];

            }]
            deliverOn:RACScheduler.mainThreadScheduler];

    [self rac_liftSelector:@selector(receiveResource:) withSignals:resourceSignal, nil];
}

- (void)receiveResource:(Resource *)resource
{
    [self.delegate authenticationController:self didReceiveResource:resource];
}
Другие вопросы по тегам