使用AFNetworking(一)

2023-11-05

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还对应着不同的请求地址、签名等方式也不同,所以统一在这个类里面去处理就好。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用AFNetworking(一) 的相关文章

随机推荐

  • 【区块链学习】

    区块链学习一 区块链概念 区块链结构 共识机制 智能合约 区块链应用于加密货币 区块链应用于实际场景 如何用最简单的方式解读区块链 通俗解释 区块链概念 区块链是一种分布式数据库 由许多区块组成 每个区块包含了一些交易信息和引用前一个区块的
  • C语言算法--桶排序

    1 什么是桶排序法 什么是桶排序法 其实说白了就是把需要排列的元素分到不同的桶中 然后我们对这些桶里的元素进行排序的一种方式 然后我们在根据桶的顺序进行元素的合并 不过前提是要确定桶的数量以及大小 按照稍微正式的说法是 桶排序法是一种基于计
  • JAVA提高ZXING对图片中的二维码的识别率(第二弹)

    背景 继上一次使用做二维码识别 公司需要高识别率的二维码识别 但ZXING实在是太弱了 对于那种二维码占比很小 图片对比度不高的 识别率低的令人咋舌 后来引入了opencv 加上恶补了一些图像处理的基础知识 总算有一个能看的过去的识别率了
  • 生产级实践之集群搭建方案系列-Redis主从、哨兵与多主集群模式的安装配置

    1 目标 介绍Redis的主从部署 哨兵部署与多主集群部署模式 实现Redis主从与哨兵集群部署 以及多主集群模式的搭建配置 2 脉络 介绍Redis的所有部署模式 特点与使用场景 完成Redis主从与哨兵集群部署 完成Redis多主集群模
  • docker搭建私有git服务器

    20210506 引言 使用git的时候 网的问题总是特别慢 反正本来也就是自己的东西 索性直接搭建一个私有的git服务器 搭建起来还是比较方便的 直接用docker搭建就好了 步骤 1 拉取镜像 docker pull gogs gogs
  • python零基础之if语句(单分支if、双分支if .else、多分支if..elif..elif..else、if语句的嵌套)、运算符优先级、比较运算符、赋值运算符、逻辑运算符、随机数random

    注意 1 在程序开发过程中 通常在判断条件时 会需要同时判断多个条件 只有多个条件都满足时 才会执行后续的代码 此时就会用到逻辑运算符 与或非了 即 and or not 2 pass 占位符 例如 当if语句内容为空的时候 如果没有代码程
  • 关于到美国学习cs的亲身感受,希望对你们有所帮助

    1 能否向各位寄托天下的朋友们简单介绍一下你自己 比如你国内的学校 或者什么档次 哪年申请出国的 什么专业 硕士还是博士 在美国的学校 或者什么档次 以及留学经历 毕业时间 现在状态 学生 博后 工作 回国 总体经历 国内在上海一所211学
  • 论文阅读:Interventional Few-Shot Learning

    作者发现两个规律 1 每一个feature dimension都携带有一定量的semantic meaning 也就是说每个feature dimension代表了一定的信息量 2 大部分的预训练模型都是以分类为训练目标 这些分类器都可以看
  • c++.net 托管字符串 非托管指针 互相转换

    System String md5String md5String admin IntPtr sptr System Runtime InteropServices Marshal StringToHGlobalAnsi md5String
  • 14款国内外精品PPT模板网站对比介绍

    简介 在平日的学习和工作中 我们经常需要制作幻灯片 通过PPT模板和素材库可以帮助我们快速设计出符合心意的幻灯片 本文对14个国内外高质量PPT模板网站进行简单的对比分析 希望对您有所帮助 国内PPT模板网站 1 SlideLive 网站地
  • 宝塔面板+花生壳实现内网部署的项目能够被在外网被访问

    05 宝塔面板 花生壳实现内网部署的项目能够被在外网被访问 1 安装操作系统 linux 2 安装宝塔面板 3 安装花生壳 4 使用宝塔面板部署项目 1 安装操作系统 linux 购买云服务器 自己安装需要的操作系统 2 安装宝塔面板 yu
  • 结构体详细了解

    用已经有的数据类型 来组合成我们需要的 复杂的数据类型 结构体定义 结构体的引入是为了描述包含有不同类型数据组合而成的数据 单独定义变量也是可以的 但变量彼此之间没有形式上的关联 容易出错 不成整体 没有封装 结构体把多个数据项打成一个包
  • 第二十八讲:神州路由器地址转换的配置

    实验拓扑图如下所示 设置内网地址段为192 168 0 0 S0 1为外网出口 通过nat访问192 168 2 0网段 操作步骤 步骤1 连接网络拓扑结构图 步骤2 配置计算机的IP地址 子网掩码和网关 步骤3 设置Router B的接口
  • app版本控制的几种方式

    是否需要做版本控制 出入参保持不变 迭代对app无感知 不需要版本控制 如果对比之前新增或者减少了参数 但是可以通过给默认值或者兼容零值的方式兼容老逻辑 不需要版本控制 其他情况需要进行版本控制 版本控制的几个方案 灰度部署 通过nginx
  • 创建一个简单的初级SpringMVC项目(非注解版)

    新建一个maven项目 JavaWeb 导入依赖
  • 人工智能——支持向量机(SVM)思维导图

    目录 1 机器学习 2 SVM 2 1 线性可分分类器 2 1 1 间隔 2 1 2 基本思想 2 1 3 目标函数 2 2 性质 2 3 线性不可分分类器 2 3 1 核函数 2 3 2 软间隔分类器 1 机器学习 2 SVM 2 1 线
  • 程序包com.aspose.words不存在&doc.getRange().replace()方法报错

    2022 7 8 周五 今天拿到一个现成项目需要部署 但是好像有lib里的东西忘给了 启动报错 程序包com aspose words不存在 阿里云仓库搜aspose words没有结果 在aspose words 15 8 0 完美解决w
  • MDK编译出现*.axf: Error: L6218E: Undefined symbol 问题解决方法

    本文参考此贴写成 原帖链接 http blog csdn net yx l128125 article details 9231385 在使用MDK编译工程时出现了如下错误 test axf Error L6218E Undefined s
  • C#读取RTSP流并且录制显示视频(PictrueBox)

    下载Nuget包 EMGU CV 引用Emgu CV public void InitVideo VideoCapture capture new VideoCapture rtsp admin 123456 192 168 0 198 5
  • 使用AFNetworking(一)

    AF是iOS中一个非常优秀的网络请求框架 下面从我个人的角度来对AF的使用做一个规范 文章目录 背景 结构图 核心的类 1 与AF框架对接的类 1 1 管理httpSessionManager 1 2 提供AP对外的API 2 发起类 2