AFNetworking 最佳实践将逻辑与视图控制器和职责分离以显示数据

Posted

技术标签:

【中文标题】AFNetworking 最佳实践将逻辑与视图控制器和职责分离以显示数据【英文标题】:AFNetworking best practice to separate logic from view controllers and responsibilities to show data 【发布时间】:2016-04-27 09:52:48 【问题描述】:

我使用AFNetworking 作为一个很好的库来使用网络。通常我有一些API 类来处理对服务器的所有请求。 API 类是一个单例,包括一些基本 url 配置,几个块(闭包)成功和失败通常将数据传回请求的控制器(对象)并更新 UI/呈现新的视图控制器等。

我的问题是可以直接在 API 类中处理警报错误。例如,如果我们收到一些错误表单服务器,我是否需要直接在 API 类中处理这些错误,或者我需要将它们传递回控制器,并且控制器基于带有错误类型的回调将显示所需的带有硬编码信息的警报。比如“您输入了错误的电子邮件或密码”等等。

这个最佳实践也是处理来自AFNetworking 的成功块或失败块的后端错误,例如当我们GET/POST/PUT/DELET 某事时。

据我所知,我们有很多可以自动处理的 http 代码错误(状态代码 400 错误请求等)。但是,如果这是自定义错误,哪种状态代码更适合这种情况,我猜答案是 - 我们应该使用 failure 回调来处理来自服务器的错误。但是如果这个自定义错误需要澄清。

总结

    第一个问题是关于根据错误向用户显示警报的实现。我们应该为此使用哪个类(或视情况而定)。 第二个问题是关于成功/失败块应该处理自定义错误,或者我们需要在AFNeteworking的失败块中每次switch错误

【问题讨论】:

【参考方案1】:

第一个问题是关于向用户显示警报的实现 关于错误。我们应该为此使用哪个类(或视情况而定)。

在这一点上,应该考虑很多选择。但我会分享我的经验。为了解决此类问题,我使用自己的类来显示应用程序中的警报。这是因为在一个类中高度自定义警报。

例子:

CustomAlertController.alert("Test title", message: "message", acceptMessage: "OK")  () -> () in


根据您使用的模式,您可以定义应该在哪里显示警报。在我的应用程序中,我使用的是 MVC。所以我的 ViewController 类应该根据 MVC 规则显示警报对话框。

例子

 MainViewController -> (Ask for data) NetworkController -> (Response with completion block) -> MainViewController -> (Show alert with response)

第二个问题是关于应该处理的成功/失败块 自定义错误,或者我们需要每次在失败块中切换错误 AFNetworking

我喜欢研究Github 上介绍的新框架 (Moya),因此我找到了一个让我了解易于使用和管理框架的愿景,它提供了处理网络请求的机会。因此,基于它们可以回答您的问题。

来自Moya的小例子:

provider.request(.Zen)  result in
    switch result 
    case let .Success(moyaResponse):
        // do something with the response data or statusCode
    case .Failure(error):
        // this means there was a network failure - either the request
        // wasn't sent (connectivity), or no response was received (server
        // timed out).  If the server responds with a 4xx or 5xx error, that
        // will be sent as a ".Success"-ful response.
    

由于可以总结使用 switch case 来处理请求结果,由于代码的高可读性和简单性,这是一个非常好的方法。

【讨论】:

感谢您的回答,所以据我所知,如果我们有来自服务器的一些失败响应,我们应该以失败方法处理它。我知道我们只需要将成功用于成功方法和失败来处理 HTTP 状态代码,据我所知,我们需要失败块来处理从服务器接收到的自定义错误。所以实际上,如果服务器向我发送了一些响应 - “您的 Alex 帐户使用在数据库中没有统计数据”,我想它没有 HTTP 状态代码 =)我应该在哪里处理它失败,对吧?这肯定是更多的后端问题。 此响应的状态码可能是 200。是的,这是更多的后端问题。我有类似的任务,我在 .成功案例,因为请求状态为 200。在成功案例中,我正在检查响应是否包含以 json 文件形式呈现的自定义错误。 是的,我问的是什么,我的问题是什么,是将处理自定义错误的逻辑置于成功或错误块的最佳方式。因为那时我可以要求后端开发人员生成成功消息而不是错误,而实际上最佳实践是我最初的问题。 好的,谢谢你的回答,我也会请马特帮忙,我在这里创建了问题:github.com/AFNetworking/AFNetworking/issues/3491【参考方案2】:

总结第 1 点: 在处理对服务器的所有请求的 API 类(AFHTTPSessionManager 子类)中,添加以下方法代码。 API 调用 ---

- (void)getMyRecordsFromServer:(NSString *)url withLastModified:(NSTimeInterval)lastModified callbackHandler:(void (^)(id, NSError *))handler 

  self.requestSerializer = [AFHTTPRequestSerializer serializer];
  self.responseSerializer = [AFHTTPResponseSerializer serializer];


  [self GET:url parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) 
    handler(responseObject, nil);

 failure:^(NSURLSessionDataTask *task, NSError *error) 

    /**********************************************************************/
    NSHTTPURLResponse *response = error.userInfo[AFNetworkingOperationFailingURLResponseErrorKey];
    NSInteger statusCode = response.statusCode;
    NSLog(@"Error Code=%ld",(long)statusCode);
    NSLog(@"Desc=%@",response.description);
    /**********************************************************************/
    [self baseFailureWithError:error onFailure:handler];

];

调用成功----

 - (void)myGetRequestWithSuccess:(void (^)(id))success
              onFailure:(void (^)(NSError *))failure
 
     [self GET:@"api/object"
           parameters:nil
           success:^(NSURLSessionDataTask *task, id responseObject)
           
                 // success code here.
            failure:^(NSURLSessionDataTask *task, NSError *error)
           
            [[self class] baseFailureWithError:error onFailure:failure];
           ];
 

之后在 baseFailureWithError:: 类方法中实现通用逻辑,然后回调您的视图控制器以实现用户特定的错误验证。 失败调用----

 - (void)baseFailureWithError:(NSError *)error
               onFailure:(void(^)(id responseObject,NSError *error))failure
 
if (failure)
    NSHTTPURLResponse *response = error.userInfo[@"AFNetworkingOperationFailingURLResponseErrorKey"];

    if ([response isKindOfClass:[NSHTTPURLResponse class]] && response.statusCode == 401)
        NSLog(@"Handle all 401's!");
        // decide show alert or not for point 2
     else 
        failure(nil, error);
    
  

【讨论】:

谢谢!是的,我明白了,但是如果我有 20 个自定义请求返回自定义错误和自定义消息,我是否需要用大量 if 块重载 baseFailureWithError? 对于常见的错误信息,您可以在另一个 if 块中显示它。但是您可以通过回调处理特定于 API 的自定义错误消息。 我通过添加 API 调用使我的答案更具描述性。方法参数可以根据需要更改。 你是如何处理 AFHTTPSessionManager 子类的这些单例?如果不是,您是否以某种方式处理该子类的释放?

以上是关于AFNetworking 最佳实践将逻辑与视图控制器和职责分离以显示数据的主要内容,如果未能解决你的问题,请参考以下文章

MVC - 数据计算最佳实践 - 视图模型与控制器

从 appdelegate 更新视图控制器 - 最佳实践?

iOS - 为帖子 + 评论数据模型设置视图控制器的最佳实践是啥?

Yii2 HOW-TO:最佳实践

在 iOS 中添加应用程序范围的标题视图的最佳实践

将领域与回收者视图一起使用的最佳实践?