0

ReactiveCocoa と Overcoat/Mantle/AFNetworking を使用してデータをフェッチし、RESTful API からユーザーを認証しています。

これは、ログイン ボタンと資格情報のテキスト フィールドを管理するログイン ビュー コントローラーのコードです。

@weakify(self);
self.loginButton.rac_command =
[[RACCommand alloc] initWithEnabled:validCredentials
                        signalBlock:^RACSignal *(id input) {
                            @strongify(self);
                            return [[PFUserManager sharedManager] logInUser:self.usernameTextField.text
                                                            password:self.passwordTextField.text];
                        }];

// Handle errors for the login command
[self.loginButton.rac_command.errors subscribeNext:^(NSError *error) {
    // Present the error message
    [PFErrorAlertFactory showOVCError:error];
}];

// Take care of the signal from the request
[[self.loginButton.rac_command.executionSignals flatten] subscribeNext:^(NSNumber *success) {
    @strongify(self);
    [self clearTextFields];
    [self.flowController controllerForMainScreen]; // Transition to "logged in state"
} error:^(NSError *error) {
    @strongify(self);
    [self clearTextFields];
}];

私はシングルトン UserManager クラスにこのメソッドを持っています:

- (RACSignal *)logInUser:(NSString *)username password:(NSString *)password {
    // Return a cold signal that sends next and complete when user is authenticated and error if authentication failed.

    PFAPIClient *client = [[PFAPIClient alloc] initWithUsername:username password:password];

    @weakify(self);
    RACSignal *loginSignal = [[client rac_POST:kAuthenticationResourcePath parameters:nil] doNext:^(OVCResponse *response) {
        @strongify(self);
        self.currentUser = response.result;
        NSError *error;
        [SSKeychain setPassword:password forService:kKeychainServiceKey account:self.currentUser.username error:&error];
        if (error) {
            [PFErrorAlertFactory showLocalizedDescriptionOfError:error];
        }
    }];

    return loginSignal;
}

この信号をボタンの RACCommand 信号として使用すると、これで問題ありません。ログインビューコントローラーで次のイベント、エラーイベント、完了イベントを処理すると、正常に動作します。

UserManager コードでわかるように、doNext ブロックで、Keychain メソッドがエラーを返した場合にエラーを表示します。このエラー処理がこのクラスに属しているかどうかについては、少し確信が持てません。

エラーはUIAlertViewとして表示されますが、このUserManagerクラスは本当にエラーを表示する責任がありますか?

rac_POST シグナルからのエラーはログイン ビュー コントローラーによって処理されます。ここでも Keychain メソッドからのエラーを処理したいと考えています。doNext ブロック内から rac_POST シグナルのサブスクライバーにエラーを送信することは可能ですか? ただし、サブスクライバーへのポインターがありません...同様に、キーチェーンメソッドでエラーが発生した場合でも、シグナルは引き続き送信されて完了し、呼び出し元のビューコントローラーが知る限り、ログインは成功します。これは明らかに本来の動作方法ではありません。

この状況全体を処理する他の好ましい方法はありますか? doNext ブロックの副作用が好ましくないことはわかっていますが、この場合、UserManager にこのメソッドを所有させ、独自の currentUser を設定できるようにするため、他の解決策はありません。これを新しいシグナルでラップし、代わりに next、complete、および error を明示的に送信する必要がありますか?

よろしく、ジェンス

4

1 に答える 1

1

カテゴリを作成できSSKeychainます:

@interface SSKeychain (RACExtension)
- (RACSignal*)rac_setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account;
@end

@implementation SSKeychain (RACExtension)

- (RACSignal*)rac_setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account 
{
    [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        NSError *error;
        BOOL result = [SSKeychain setPassword:password forService:service account:account error:&error];
        if (result) {
            [subscriber sendNext:@(result)];
            [subscriber sendCompleted];
        } else {
            [subscriber sendError:error];
        }
    }];
}

@end

そして、あなたのそれを使用してくださいUserManager

- (RACSignal *)logInUser:(NSString *)username password:(NSString *)password 
{
    PFAPIClient *client = [[PFAPIClient alloc] initWithUsername:username password:password];

    @weakify(self);
    return [[[client rac_POST:kAuthenticationResourcePath parameters:nil]
             flattenMap:^RACStream *(OVCResponse *response){
                 @strongify(self)
                 return [[SSKeychain rac_setPassword:password forService:kKeychainServiceKey account:self.currentUser.username]
                         mapReplace:response];
             }]
             doNext:^(OVCResponse *response){
                 @strongify(self)
                 self.currentUser = response.result;
             }]
}
于 2015-06-11T06:39:15.123 に答える