0

我最近刚从 ASIHttpRequest 库切换到 AFNetworking。我真的很喜欢这种简单性,但我仍然在努力理解如何构建我的异步代码。

请考虑此注册方案。

  • 首先,我想检查输入的电子邮件地址是否可用。
  • 接下来我要检查输入的用户名是否可用。
  • 如果以上两者都有效且可用,我想提交我的真实注册请求。

我的代码看起来像。

- (void)signUp{

    BOOL hasValidEmail = [self validateEmail:email];
    BOOL hasValidUsername = [self validateUsername:username];

    if(!hasValidEmail){
        NSLog(@"Invalid email");
        return;
    }

    if(!hasValidUsername){
        NSLog(@"Invalid username");
        return;
    }

    if (hasValidEmail && hasValidUsername) {
        NSLog(@"Go ahead and create account");
    }
}

考虑到我的网络方法的异步性质,我不太确定如何构建它。通常会在前两次可用性检查收到响应之前达到最后一个条件。

可用性检查方法如下所示:

- (BOOL)validateEmail:(NSString*)email{

    __block NSString* emailAlreadyExists = @"";

    NSString* url = [NSString stringWithFormat:@"user/emailexists/%@", email];

    [[APIClient sharedClient] getPath:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {

        emailAlreadyExists = [responseObject valueForKey:@"exists"];

    } failure:^(AFHTTPRequestOperation *operation, NSError* error) {
        NSLog(@"Email availability check failed: %@", error.localizedDescription);
    }];

    if([emailAlreadyExists isEqualToString:@"true"]){
        return NO;
    }

    return YES;   
}

也许只是我的积木技能需要提高,但我真的很想听听您将如何构建这样的场景?

虽然代码示例会“不错”,但我真的在寻找您知道的模式或好的技术。

谢谢。

4

1 回答 1

0

我通常将这些事情分解成步骤,并在前一个成功时开始下一步。为此目的,块非常适合传递。

这显然不会编译,但希望它可以让您了解如何执行此操作:

typedef enum
{
    SignupErrorNetworkError,
    SignupErrorEmailAlreadyTaken,
    SignupErrorUsernameAlreadyTaken,
} SignupError;

typedef enum
{
    // These steps must be performed in this order.
    SignupStepValidateEmail,
    SignupStepValidateUsername,
    SignupStepCreateAccount,
} SignupStep;

typedef void (^SignupSuccessBlock)();
typedef void (^SignupFailureBlock)(SignupError reason);

// Call this to sign up.
- (void)signupWithSuccess:(SignupSuccessBlock)success failure:(SignupFailureBlock)failure
{
    // Start the first step of the process.
    [self performSignupStep:SignupStepValidateEmail success:success failure:failure];
}

// Internal method. Don't call this from outside.
- (void)performSignupStep:(SignupStep)step success:(SignupSuccessBlock)success failure:(SignupFailureBlock)failure
{
    switch (step)
    {
        case SignupStepValidateEmail:
            [[APIClient sharedClient] getPath:url parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
                if ([responseObject valueForKey:@"exists"])
                {
                    if (failure) failure(SignupErrorEmailAlreadyTaken);
                    return;                }
                }
                // Start next async step in signup process.
                [self performSignupStep:SignupStepValidateUsername success:success failure:failure];
            } failure:^(AFHTTPRequestOperation *operation, NSError* error) {
                if (failure) failure(SignupErrorNetworkError);
            }];
            break;
        case SignupStepValidateUsername:
            // Similar to the step above. Starts the create account step on success.
            break;            
        case SignupStepCreateAccount:
            // Similar to the step above. Call the success block when done.
            break;            
    }
}

如果开关变得太长和丑陋,您还可以将步骤变成单独的方法并删除步骤枚举:validateEmailWithSuccess:failure通过调用validateUsernameWithSuccess:failure等继续。

我只是想强调上面示例中进程的状态机性质。

于 2012-04-24T20:00:23.693 回答