NTApiClient 1.14

NTApiClient 1.14

测试测试
Lang语言 Obj-CObjective C
许可 MIT
发布上次发布2014年12月

Ethan Nagel 维护。



  • Ethan Nagel 和 Jacob Knobel

一个简单灵活的基于 JSON 的 iOS API 客户端。

NTApiClient 旨在用作您自己的 JSON API 的基类。NTApiClient 最终使用 NSURLRequest 执行其处理。请求处理在后台线程上处理,您的响应处理可以是后台线程、主线程或启动请求的线程(当前线程)。

NTArgs

该系统的核心是通过一个 NTApiArgs 数组(论证可能是简单项目,如 URL 论证或表单参数)进行控制的。NTApiArgs 还可用于设置标头或控制整体请求,例如超时或处理响应的线程。因为这是一个可以构建和处理的数组,它提供了大量的灵活性。您可以认为论证数组是一个非常特定的 DSL。以下 NTArgs 支持

  • 传递数据
    • NTApiUrlArg - 查询字符串值
    • NTApiFormArg - 表单值(POST 语义)
    • NTApiMultipartArg - 多部分附件
    • NTApiRawDataArg - 发送不需要额外处理的数据
    • NTApiBaseUrlArg - 覆盖默认的 baseUrl
  • 设置标头
    • NTApiHeaderArg - 标头值
    • NTApiBasicAuthArg - 基本认证标头
  • 控制整体请求
    • NTApiOptionArg - 设置多个整体选项,包括使用的线程模型。
    • NTApiTimeoutArg - 覆盖默认的超时
    • NTApiHttpMethodArg - 覆盖默认的 HTTP 方法。
    • NTApiCachePolicyArg - 覆盖默认的缓存策略。

默认值

默认值允许您设置可能根据请求覆盖的全局值。这是一个设置类似于请求的 baseUrl 等值非常方便的方法。此外,可以通过协议实现默认值以提供“动态”默认值,例如会话 ID。

线程

每个请求都使用多个线程进行处理

  1. 在调用线程上创建请求(解析 NTApiArgs 到 NSURLRequest
  2. NSURLRequest 代理处理在一个共享的后台线程(NTApiRequestThread)上处理。此处理限于捕获下载数据、捕获错误等。如果已设置上传或下载进度处理程序,这些将在指示的线程上调用 - 而不是 NTApiRequestThread。
  3. 一旦请求处理完成,就会在单独的后台线程上开始响应处理。这是解析 JSON 的地方。
  4. 最后,会根据指示的线程(主线程、后台线程或原始请求的线程)调用你的responseHandler函数。

对于每一个请求,都会启动一个iOS后台任务,该任务会持续到你的responseHandler函数返回,所以即使应用程序进入后台状态,你的任务也应该可以正常完成。

日志记录

良好的日志记录对API来说非常重要。默认情况下,NTApi会将消息通过-overridable方法 -(void)writeLogWithType:andFormat:记录到NSLog中。你可以重写这个方法以记录到你喜欢的日志子系统。此外,如果你使用CocoaPods并安装了NTLog,NTAPIClient将自动使用它。你也可以通过宏禁用所有日志。(参见NTAPI_LOG_*宏。)如果你感兴趣,可以查看NTLog的配置并发送一个Pull Request!

你可以使用logFlags属性编程控制记录的内容。这可以针对每个实例设置,也可以使用“logFlags”默认值全局设置。

版本历史

  • 1.10 - 2014年3月14日 添加了对自签名SSL证书的支持,使用 arg [NTApiOptionArg optionAllowInvalidSSLCert:YES]

  • 1.00 - 2014年2月7日 第一版提交到cocoapods。添加了文档和不错的示例。

传统安装方法

只需将Core文件夹中的文件添加到你的项目中,就可以享受它了!

使用说明

你的API类

NTApiClient被设计为可以继承来自定义API。你可以查看示例应用程序以获得如何做的良好示例。所有对NTApiClient的困难工作都是通过beginRequest:方法来完成的 - 你通常会想要用处理你共享API逻辑的方法来包装这个方法。实际上,我推荐使用两个方法,如下所示

-(NTApiRequest *)beginDirectRequest:(NSString *)command args:(NSArray *)args responseHandler:(void (^)(NSDictionary *data, NTApiError *error))responseHandler
{
    // This method should do whatever is common to ALL requests - generally extracting error messages or adding args that are common to all requests. 

    NTApiRequest *request = [self beginRequest:command args:args responseHandler:^(NTApiResponse *response)
    {
        NTApiError *error = response.error;

        // If there wasn't a system error, see if we can find an error from the API and instantiate it...

        if ( !error )
        {
            /// TODO: Extract any API error code that was returned
        }

        responseHandler(response.json, error);
    }];

    return request;
}

-(NTApiRequest *)beginStdRequest:(NSString *)command args:(NSArray *)args responseHandler:(void (^)(NSDictionary *data, NTApiError *error))responseHandler
{
    // This method should handle items that are generally common to requests. It may add standard parameters such
    // as a session token or even make multiple API calls to do something like re-authenticate transparently. It should
    // make one or more calls to beginDirectRequest.

    return [self beginDirectRequest:command args:args responseHandler:responseHandler];
}

你的API方法通常应该调用beginStdRequest来利用你的标准请求处理。以下是一个示例应用程序中API方法的示例,以供你参考。我们在将响应解析到我们的业务对象中并返回它们。

-(NTApiRequest *)beginFindCitiesWithName:(NSString *)cityName searchType:(OpenWeatherSearchType)searchType maxItems:(int)maxItems  responseHandler:(void (^)(NSArray *currentWeatherItems, NTApiError *error))responseHandler
{
    return [self beginStdRequest:@"find"
                            args:@[
                                   [NTApiUrlArg argWithName:@"q" string:cityName],
                                   [NTApiUrlArg argWithName:@"type" string:searchType],
                                   [NTApiUrlArg argWithName:@"cnt" intValue:maxItems],
                                   ]
                 responseHandler:^(NSDictionary *data, NTApiError *error)
    {
        NSArray *currentWeatherItems = nil;

        if ( data )
        {
            NSArray *jsonItems = [data arrayForKey:@"list"];

            currentWeatherItems = [CurrentWeather itemArrayWithJsonArray:jsonItems];
        }

        responseHandler(currentWeatherItems, error);
    }];
}

你可以在init方法中加载它们的默认值来实施你自己的默认设置。这允许客户端代码根据需要在每个API客户端实例的基础上覆盖它们。此示例也来自示例应用程序。

-(id)init
{
    self = [super init];

    if ( self )
    {
        self.appid = [self.class getDefault:@"appid"];
    }

    return self;
}

如果你希望将你的API错误代码转换为“NSString enum's”,你可以通过NTApiError将它们注册。这将自动将它们转换为你的常量值,这样你就可以使用==代替isEqualToString:。最佳做法是在`+load`方法中这样做

+(void)load
{
    [NTApiError addErrorCode:OpenWeatherErrorCodeNotFound];
}

初始化

通常,你希望在应用程序启动时将API服务器等配置为默认值。这可以在你的AppDelegate的didFinishLoadingWithOptions中完成,如下所示

[OpenWeatherApiClient setDefault:@"baseUrl" value:@"http://api.openweathermap.org/data/2.5"];
[OpenWeatherApiClient setDefault:@"appid" value:OPENWEATHER_APPID];

制作API调用

使用API非常直接,以下是一个示例。(注意,这假设有一个助手来创建实例 - +apiClient

[[OpenWeatherApiClient apiClient] beginFindCitiesWithName:cityName
                                               searchType:OpenWeatherSearchTypeLike
                                                 maxItems:20
                                          responseHandler:^(NSArray *currentWeatherItems, NTApiError *error)
{
    if ( error ) // display API errors
    {
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error getting weather"
                                                message:error.errorMessage
                                                           delegate:nil
                                                  cancelButtonTitle:@"Ok"
                                                  otherButtonTitles:nil];

        [alertView show];

        return ;
    }

    self.currentWeatherItems = currentWeatherItems;
    [self.tableView reloadData];
}];