当前位置: 首页 > 工具软件 > AFNetworking > 使用案例 >

使用AFNetworking(一)

濮阳景天
2023-12-01

AF是iOS中一个非常优秀的网络请求框架,下面从我个人的角度来对AF的使用做一个规范。

背景

很早以前或许你还用过ASIHTTPRequest,后来就大都使用AFNetworking了,AF采用block的形式将请求结果返回,这样管理起来我感觉比ASI更加便捷。

一直使用AF,但是从来没有对AF有一个规范化的使用。经常是一个请求类里面即有AF的代码又有业务代码,搞得乱七八糟。

结构图

回头补上…

核心的类

本着尽量业务解耦的原则和功能单一性原则,分为三类规范AF的使用。

第一,与AF框架对接的类;第二,项目里的网络请求的管理类;第三,协助第二部分进行业务处理的类。

1. 与AF框架对接的类

通过此类管理AF,这个类是一个桥梁。

项目中发起网络请求的类不要直接去引用AF,因为此类里面会有业务代码,再加上AF的代码的话,就会出现功能一个类承担,杂糅、乱糟糟的情况。

首先我们需要一个”桥梁“来对接业务代码与AF。创建一个管理AFHTTPSessionManager的类,架起AF与业务类之间的桥梁。

这个类的主要作用有两个,1. 管理httpSessionManager 2. 对业务提供AF的请求接口。

#import "ENHTTPSessionManager.h"
#import "AFHTTPSessionManager.h"

static AFHTTPSessionManager *httpSessionManager = nil;

@implementation ENHTTPSessionManager

+ (AFHTTPSessionManager *)httpSessionManager {
    if (httpSessionManager == nil) {
        // httpSessionManager
        httpSessionManager = [[AFHTTPSessionManager alloc] init];
        // request out time set
        httpSessionManager.requestSerializer.timeoutInterval = 20.f;
        // response serializer set
        AFHTTPResponseSerializer *responseSerializer = [AFHTTPResponseSerializer serializer];
        responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/xml", @"text/plain", nil];
        httpSessionManager.responseSerializer = responseSerializer;
        // none securityPolicy
        httpSessionManager.securityPolicy = [AFSecurityPolicy policyWithPinningMode:AFSSLPinningModeNone];
    }
    return httpSessionManager;
}

+ (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
    return [[ENHTTPSessionManager httpSessionManager] POST:URLString parameters:parameters headers:nil progress:uploadProgress success:success failure:failure];
}

+ (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
    return [[ENHTTPSessionManager httpSessionManager] GET:URLString parameters:nil headers:nil progress:downloadProgress success:success failure:failure];
}
@end

1.1 管理httpSessionManager

我们使用AF的时候直接使用AFHTTPSessionManager这个类发起网络请求。

创建一个类来管理httpSessionManager对象,我们将httpSessionManager处理成全局的单例对象

+ (AFHTTPSessionManager *)httpSessionManager {
    if (httpSessionManager == nil) {
   	    //...
    }
    return httpSessionManager;
}

这样做的目的有两个:

  1. 项目中每个网络请求比较多,每次都要初始化一次对象和设置的话,不如直接初始化一次,对相关的设置做一次配置之后就可以每次使用了。将httpSessionManager统一的管理起来。

  2. 有关AF使用检测发现内存问题的解决,为什么会检测出内存问题,这个不是AF的问题,这里不赘述了,看这里

1.2 提供AP对外的API

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

/// This class is a manager of the HTTP request, there will use AFNetworking send a request in this class. Never add the App business code in this class.
@interface ENHTTPSessionManager : NSObject

+ (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
                             parameters:(nullable id)parameters
                               progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
                                success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                                failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

+ (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
                              progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
                               success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
                               failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

@end

2. 发起类

业务使用该类发起网络请求

先来看一下完整的代码:

ENHTTPRequestManager.h

#import <Foundation/Foundation.h>
#import "ENHTTPHelper.h"

NS_ASSUME_NONNULL_BEGIN

/// Ues this class to send the http request and manager the request task.
@interface ENHTTPRequestManager : NSObject

// init
+ (instancetype)manager;

// sen a request for GET
- (void)GETWithAPICommand:(ENAPICommand)command
               parameters:(nullable id)parameters
                 progress:(nullable void (^)(NSProgress *progress))progressCallback
                  success:(nullable void (^)(NSDictionary *reponse))successCallback
                  failure:(nullable void (^)(NSError *error))failureCallback;

// the requesting task set.
@property (nonatomic, strong, readonly) NSMutableSet *taskSet;

// cancel a request, cancel the request when dismiss before controller.
- (void)cancelTask:(ENAPICommand)command;

@end

NS_ASSUME_NONNULL_END

ENHTTPRequestManager.m

#import "ENHTTPRequestManager.h"
#import "ENHTTPSessionManager.h"

@interface ENHTTPRequestManager ()

@property (nonatomic, strong, readwrite) NSMutableSet *taskSet;

@end

static ENHTTPRequestManager *_manager = nil;

@implementation ENHTTPRequestManager

- (NSMutableSet *)taskSet {
    if (!_taskSet) {
        _taskSet = [[NSMutableSet alloc] init];
    }
    return _taskSet;
}

+ (instancetype)manager {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _manager = [[ENHTTPRequestManager alloc] init];
    });
    return _manager;
}

- (void)GETWithAPICommand:(ENAPICommand)command
               parameters:(nullable id)parameters
    progress:(nullable void (^)(NSProgress *progress))progressCallback
                  success:(nullable void (^)(NSDictionary *reponse))successCallback
                  failure:(nullable void (^)(NSError *error))failureCallback
{
    __weak typeof(self)weakSelf = self;
    NSString *URLString = [ENHTTPHelper GETURLStringWithAPICommand:command parameters:parameters];
#ifdef DEBUG
    NSLog(@"GET REQUEST : %@",URLString);
#else
    
#endif
    NSURLSessionDataTask *task = [ENHTTPSessionManager GET:URLString progress:progressCallback success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
        
        NSString *jsonString = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding];
        NSData *jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
        NSError *err;
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&err];
        dispatch_async(dispatch_get_main_queue(), ^{
#ifdef DEBUG
            NSLog(@"GET RESPONSE : %@",dict);
#else
    
#endif
            if (successCallback) {
                successCallback(dict);
            }
            [weakSelf removeTask:task];
        });
        
    } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
        dispatch_async(dispatch_get_main_queue(), ^{
#ifdef DEBUG
            NSLog(@"GET ERROR : %@",error);
#else
    
#endif
            if (failureCallback) {
                failureCallback(error);
            }
            [weakSelf removeTask:task];
        });
    }];
    
    NSString *taskDescription = [ENHTTPHelper APIWithAPICommand:command];
    if (taskDescription) {
        task.taskDescription = taskDescription;
        [self.taskSet addObject:task];
    }
}



#pragma mark - task manager

- (void)cancelTask:(ENAPICommand)command
{
    NSString *taskDescription = [ENHTTPHelper APIWithAPICommand:command];
    for (NSURLSessionDataTask *task in self.taskSet) {
        if ([task.taskDescription isEqualToString:taskDescription]) {
            if (task.state < NSURLSessionTaskStateCanceling) {
                [task cancel];
            }
            [self.taskSet removeObject:task];
        }
    }
}

- (void)removeTask:(NSURLSessionDataTask *)task
{
    if ([self.taskSet containsObject:task]) {
        [self.taskSet removeObject:task];
#ifdef DEBUG
        NSLog(@"TASK REMOVE : %@",task.taskDescription);
#else
    
#endif
    }
}
- (void)addTask:(NSURLSessionDataTask *)task
{
    [self.taskSet addObject:task];
}

@end

这个类有以下几个作用:

2.1 向业务提供网络请求的API

向外提供了GET、POST请求的API和取消网络任务的API,这都是基础功能。

这里以一个GET请求为例,业务层使用ENHTTPRequestManager这个类直接发起网络请求。

请求内部是对参数等的处理,这些处理,我们借助另一个类,ENHTTPHelper,在这个类里面处理所有的参数拼接等的事情。

2.2 管理网络请求的task任务

我们用taskSet来管理所有的网络请求的task,发起任务,任务完成/取消,删除任务对象。

管理任务的目的很简单,就是为了能够在想取消任务的时候直接取消。这种业务场景适用在界面退出前,先取消正在进行中的请求。

2.3 发网络请求,处理请求结果

发起网络请求,处理一下返回的data,将数据转成json,将json转成字典返回出去。

3. 协助类

协助发起类处理一些其他事宜

协助类提供放一些API的定义,协助发起类处理一些其他事宜,例如定义Api,GET请求拼接URL等的工作。

ENHTTPHelper.h

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

typedef NS_ENUM(NSUInteger, ENAPICommand) {
		...
};

/// This class assists ENHTTPRequestManager to construct a complete network request. There will more App business in this class.
@interface ENHTTPHelper : NSObject

// Get the request URL for GET request
+ (NSString *)GETURLStringWithAPICommand:(ENAPICommand)command
                              parameters:(NSDictionary *)parameters;

// Get the API description by API command
+ (NSString *)APIWithAPICommand:(ENAPICommand)command;

@end

ENHTTPHelper.m

#import "ENHTTPHelper.h"

@interface ENHTTPHelper ()

@property (nonatomic, strong, readwrite) NSDictionary *commonParameters;

@end

@implementation ENHTTPHelper

+ (NSString *)GETURLStringWithAPICommand:(ENAPICommand)command
                              parameters:(NSDictionary *)parameters
{
    NSMutableDictionary *allParameters = [NSMutableDictionary dictionary];
    
    // protocol. http / https
    NSString *protocol = [ENHTTPHelper protocol];
    
    // domain. xxx.xxx.com
    NSString *domain = [ENHTTPHelper domainWithAPICommand:command];
    
    // path
    NSString *URLSubpath = [ENHTTPHelper pathWithAPICommand:command];

    // common parameters
    NSMutableDictionary *commonParameters = [ENHTTPHelper commonParametersWithAPICommand:command];
    
    // other parameters
    if (ENSettings.mainSettings.location && ENSettings.mainSettings.location.length) {
        commonParameters[@"LocationID"] = ENSettings.mainSettings.location;
    }
    if (ENSettings.mainSettings.sessionID && ENSettings.mainSettings.sessionID.length) {
        commonParameters[@"SessionID"] = ENSettings.mainSettings.sessionID;
    }
    
    // add all parameters
    [allParameters addEntriesFromDictionary:parameters];
    [allParameters addEntriesFromDictionary:commonParameters];


    // structure queryItems
    NSMutableArray *queryItems = [NSMutableArray arrayWithCapacity:allParameters.count];
    for(NSString *name in allParameters)
    {
        NSString *value = allParameters[name];
        NSURLQueryItem *item = [NSURLQueryItem queryItemWithName:name value:value];
        [queryItems addObject:item];
    }
    
    // structure urlComponents
    NSURLComponents *urlComponents = [[NSURLComponents alloc] init];
    urlComponents.scheme = protocol;
    urlComponents.host = domain;
    urlComponents.path = URLSubpath;
    urlComponents.queryItems = queryItems;
    
    // return urlComponents' URL string
    return urlComponents.URL.absoluteString;
}


#pragma mark -

+ (NSString *)protocol
{
   ...
}

+ (NSString *)domainWithAPICommand:(ENAPICommand)command
{
    ...
}

+ (NSString *)pathWithAPICommand:(ENAPICommand)command
{
    ...    
}

+ (NSString *)APITokenWithAPICommand:(ENAPICommand)command
{
    ...
}

+ (NSMutableDictionary *)commonParametersWithAPICommand:(ENAPICommand)command
{
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
    dict[@"APIToken"] = [ENHTTPHelper APITokenWithAPICommand:command];
    dict[@"APICommand"] = [ENHTTPHelper APIWithAPICommand:command];
    dict[@"UserID"] = [ENHTTPHelper userIDWithAPICommand:command];
    dict[@"Version"] = [ENSettings mainSettings].version;
    dict[@"r"] = [ENTools timeStamp];
    return dict;
}

+ (NSString *)userIDWithAPICommand:(ENAPICommand)command
{
	...
}

+ (NSString *)APIWithAPICommand:(ENAPICommand)command
{
    switch (command)
    {
    	 ...
    }
}
@end

ENAPICommand是将API定义成立一个枚举,提供了通过枚举获取对应API的接口,或者项目里直接使用宏都是可以的。

这个类的实现根据不同业务去提供不同API供请求类使用。我这里的业务比较繁琐,不同的API还对应着不同的请求地址、签名等方式也不同,所以统一在这个类里面去处理就好。

 类似资料: