iOS 网络请求: Alamofire 结合 ObjectMapper 实现自动解析

引言

在 iOS 开发中,网络请求是常见且致其重要的功能之一。从获取资料到上传数据,出色的网络请求框架能夠大大提升开发效率。

Alamofire 是一个极具人气的 Swift 网络请求框架,提供了便据的 API 以完成网络请求和响应处理。它支持多种请求类型,如 GET 和 POST,并且给予您便据的带容处理过滤器和返回数据解析的功能。

ObjectMapper 是一个强大的 Swift 数据映射工具,使用其提供的 Mappable 协议,可以将 JSON 数据自动映射到 Swift 模型中,大大简化解析代码和出错处理。

本文将介绍如何使用 Alamofire 和 ObjectMapper,完成 GET 和 POST 请求,并将返回数据自动解析为 Swift 模型。通过实战和代码示例,帮助您快速熟练这两种工具,从而提升开发效率。

Alamofire发起GET、POST请求

Alamofire为我们提供了十分简洁的数据请求方法,还可以根据数据的返回类型解析不同的响应体,我们以JSON格式为例。只需要设置请求地址、请求方式、请求参数、编码方式以及请求头。

GET请求

Swift 复制代码
AF.request(url, method: .get, parameters: parameters, encoding: URLEncoding.default, headers: endpoint.headers).responseJSON { (response) in
            switch response.result {
            case .success:
                if let json = response.value as? [String: Any] {
                    ...
                } else {
                    // 数据为空 或者格式不对
                    let error = NSError(domain: "com.mw.network.error", code: MWNetworkDefine.clientErrorCode_dataFormat, userInfo: [NSLocalizedDescriptionKey: "数据为空或者格式不对"])
                }
            case .failure(let error):
                let mwError = MWNetError(error: error,isCanceled: error.isExplicitlyCancelledError)
                completion(nil, nil, mwError)
            }
        }

POST请求

Swift 复制代码
AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: endpoint.headers).responseJSON { (response) in
            switch response.result {
            case .success:
                if let json = response.value as? [String: Any] {
                   ....
                } else {
                    // 数据为空 或者格式不对
                    let error = NSError(domain: "com.mw.network.error", code: MWNetworkDefine.clientErrorCode_dataFormat, userInfo: [NSLocalizedDescriptionKey: "数据为空或者格式不对"])
  
                }
            case .failure(let error):
                let mwError = MWNetError(error: error,isCanceled: error.isExplicitlyCancelledError)
                mainThread(model: nil, data: nil, error: mwError, completion: completion)
            }
        }

ObjectMapper进行数据解析

使用ObjectMapper进行数据解析成模型时,需要模型遵循Mappable协议,并手动创建数据对应的键值映射。

假设有以下的json数据:

Swift 复制代码
{
    "id": 123,
    "name": "John Doe",
    "email": "john.doe@example.com"
}

那我们需要可以创建一个对应的数据模型:

Swift 复制代码
import ObjectMapper

class User: Mappable {
    var id: Int?
    var name: String?
    var email: String?

    required init?(map: Map) {}

    func mapping(map: Map) {
        id    <- map["id"]
        name  <- map["name"]
        email <- map["email"]
    }
}

然后借助Mapper实现解析:

Swift 复制代码
func parseUserData(json: [String: Any]) {
    if let user = Mapper<User>().map(JSON: json) {
        print("User ID: \(user.id ?? 0)")
        print("Name: \(user.name ?? "No Name")")
        print("Email: \(user.email ?? "No Email")")
    } else {
        print("Failed to parse user data")
    }
}

// 示例调用
let jsonData: [String: Any] = [
    "id": 123,
    "name": "John Doe",
    "email": "john.doe@example.com"
]

parseUserData(json: jsonData)

Alamofire结合ObjectMapper实现接口数据自动解析

而在实际的项目开发中往往我们并不会直接使用Alamofire进行网络的请求,而是会进一步封装增加一层,在这一层中呢我们可以设置一些公共参数,公共请求头,请求加密处理,服务端环境切换等等等等工作,包括数据的处理和自动解析当然也可以在这一层来进行。

以我们当前的项目为例,为了处理网络的请求专门创建了一个名为MWNetworkHelper的类,它负责文件的上传、文件的下载、非加密的GET请求,非加密的POST请求,加密的GET请求、加密的POST请求,以及请求后的数据处理和错误处理。

接下来我们就以非加密的POST请求为例来分析Alamofire结合ObjectMapper实现接口数据自动解析。

项目中的所有请求通过MWNetworkHelper的类方法发送:

public class func request<T: Mappable>(endpoint: MWAPIProtocol, parameters: [String: Any ]?,modelType:T.Type ? = MWNetEmptyData.self , completion: ((_ model:T?,_ data:Any?, MWNetError?) -> Void)?) -> DataRequest?

该方法的具体实现如下:

Swift 复制代码
    /// 发起请求
    /// - Parameters:
    /// - endpoint: 请求地址,枚举
    /// - parameters: 请求参数
    /// - completion: 请求完成回调
    /// - T: 返回数据模型
    /// - return: 请求
    @discardableResult
    public class func request<T: Mappable>(endpoint: MWAPIProtocol, parameters: [String: Any]?,modelType:T.Type? = MWNetEmptyData.self, completion: ((_ model:T?,_ data:Any?, MWNetError?) -> Void)?) -> DataRequest? {
        let completion = completion ?? {_,_,_ in }
        // 如果是断网状态直接返回
        if !MWNetworkManager.shared.isReachable {
            let error = NSError(domain: "com.mw.network.error", code: MWNetworkDefine.clientErrorCode_noNetwork, userInfo: [NSLocalizedDescriptionKey: "网络不可用"])
            let mwError = MWNetError(error: error,isCanceled: false)
            MWToast.showToast("网络不可用")
            mainThread(model: nil, data: nil, error: mwError, completion: completion)
            return nil
        }
        
        if let encryEndpoint = endpoint as? MWAPIEncryEndpoint{
            return requestEncrypt(endpoint: encryEndpoint, parameters: parameters, modelType: modelType, completion: completion)
        } else if let normalEndpoint = endpoint as? MWAPINormalEndpoint {
            return requestNormal(endpoint: normalEndpoint, parameters: parameters, modelType: modelType, completion: completion)
        } else {
            fatalError("endpoint is not MWAPIProtocol")
        }
    }
  1. 该方法首先判断了网络状态,如果处于断网状态直接回调回error数据。
  2. 接下来根据枚举所属的类判断发起的是加密请求还是非加密请求。

加密请求和非加密请求都还会再次分割为GET请求和POST的请求,我们直接过渡到非加密的POST请求。

Swift 复制代码
    /// 发起非加密的POST请求
    /// - Parameters:
    /// - endpoint: 请求地址,枚举
    /// - parameters: 请求参数
    /// - completion: 请求完成回调
    /// - T: 返回数据模型
    /// - return: 请求
    @discardableResult
    public class func requestNormalPOST<T: Mappable>(endpoint: MWAPINormalEndpoint, parameters: [String: Any]?, modelType:T.Type? = MWNetEmptyData.self, completion: @escaping (_ model:T?,_ data:Any?, MWNetError?) -> Void) -> DataRequest {
        MWLogHelper.debug("非加密POST=请求地址:\(endpoint.domain + endpoint.path) 请求参数:\(String(describing: parameters))", context: "Network")
        let url = endpoint.domain + endpoint.path
        return AF.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: endpoint.headers).responseJSON { (response) in
            switch response.result {
            case .success:
                if let json = response.value as? [String: Any] {
                    dealRequestJsonData(endpoint: endpoint, data: json, completion: completion)
                } else {
                    // 数据为空 或者格式不对
                    let error = NSError(domain: "com.mw.network.error", code: MWNetworkDefine.clientErrorCode_dataFormat, userInfo: [NSLocalizedDescriptionKey: "数据为空或者格式不对"])
                    let mwError = MWNetError(error: error,isCanceled: false)
                    mainThread(model: nil, data: nil, error: mwError, completion: completion)
                }
            case .failure(let error):
                let mwError = MWNetError(error: error,isCanceled: error.isExplicitlyCancelledError)
                mainThread(model: nil, data: nil, error: mwError, completion: completion)
            }
        }
    }
  1. 利用Swift的特性我们创建了一个泛型,通过T:Mappable指定了返回数据模型必须遵循ObjectMapper的Mappable协议,确保可以进行JSON映射,灵活支持不同的返回类型。
  2. MWAPINormalEndpoint是一个非加密请求的枚举,枚举内包含了很多内容包括请求的域名或根据环境进行切换,包括具体的请求接口以及公共请求头等等信息。
  3. 对于一个成功的请求,调用dealRequestJsonData方法开始处理数据。
  4. 而对于一个失败的请求,我们直接回调回error信息。

对于数据处理的方法dealRequestJsonData,由于项目庞大的原因,它仍然包含了很多内容,但是我们可以把重点直接集中到简单的部分:

Swift 复制代码
    /// 处理数据
    /// - Parameters:
    /// - data: 数据
    /// - completion: 完成回调
    /// - T: 返回数据模型
    private class func dealRequestJsonData<T: Mappable>(endpoint: MWAPIProtocol,data:[String:Any],modelType:T.Type? = nil,completion: @escaping (_ model:T?,_ data:Any?, MWNetError?) -> Void) {
        var data = data
        // 如果是加密数据
           ....

        if endpoint.isDirectParse {
            // 错误结果
            if let code = data["code"] as? Int {
                 ...
                return
            }
            // 直接解析
            let model = Mapper<T>().map(JSON: data)
            mainThread(model: model, data: data, error: nil, completion: completion)
        } else {
          ....

        }
    }
    
    /// 主线程回调
    /// - Parameters:
    /// - modelType: 数据模型类型
    /// - model: 数据模型
    /// - data: 数据
    /// - error: 错误
    /// - completion: 完成回调
    private class func mainThread<T: Mappable>(modelType:T.Type? = nil,model:T?,data:Any?,error:MWNetError?,completion: @escaping (_ model:T?,_ data:Any?, MWNetError?) -> Void) {
        DispatchQueue.main.async {
            completion(model, data, error)
        }
    }
  1. 因为服务端并不一定会只返回一种结构的数据格式,我们只考虑直接解析这一种。
  2. 直接通过let model = Mapper<T>().map(JSON: data)获取到我们需要的数据模型。
  3. 在主线程中回调回数据。

结语

在现代 iOS 应用开发中,处理网络请求和数据解析是一项基础但极具挑战的任务。本文通过 Alamofire 和 ObjectMapper 的结合,展示了如何构建灵活且高效的网络请求与数据解析架构。从发起请求到解析数据,再到错误处理的全流程,我们看到了这两种工具如何相辅相成,极大提升了开发效率。

然而,开发并非一成不变,实际应用场景中,可能会涉及到更多复杂的需求,例如分页加载、文件上传、错误重试等。希望本篇内容为你的项目提供启发,也欢迎你探索和扩展 Alamofire 与 ObjectMapper 的更多潜力。

相关推荐
开心就好20256 小时前
iOS App 安全加固流程记录,代码、资源与安装包保护
后端·ios
开心就好20256 小时前
iOS App 性能测试工具怎么选?使用克魔助手(Keymob)结合 Instruments 完成
后端·ios
zhongjiahao1 天前
面试常问的 RunLoop,到底在Loop什么?
ios
wvy2 天前
iOS 26手势返回到根页面时TabBar的动效问题
ios
RickeyBoy2 天前
iOS 图片取色完全指南:从像素格式到工程实践
ios
aiopencode3 天前
使用 Ipa Guard 命令行版本将 IPA 混淆接入自动化流程
后端·ios
二流小码农3 天前
鸿蒙开发:路由组件升级,支持页面一键创建
android·ios·harmonyos
iceiceiceice4 天前
iOS PDF阅读器段评实现:如何从 PDFSelection 精准还原一个自然段
前端·人工智能·ios
ssshooter5 天前
Tauri 踩坑 appLink 修改后闪退
前端·ios·rust
二流小码农5 天前
鸿蒙开发:上传一张参考图片便可实现页面功能
android·ios·harmonyos