ESNetworkManager 2.0

ESNetworkManager 2.0

Mahmoud Eissa 维护。



  • Mahmoud Eissa

ESNetworkManager 是一个依赖 Alamofire 的库,使得创建新的工作请求变得容易,并支持响应映射。

版本兼容性

以下是当前 Swift 兼容性分解

Swift 版本 网络版本
5.X master

安装

CocoaPods

将以下内容添加到您的 Podfile

pod 'ESNetworkManager'
pod 'ESNetworkManager/Core'
pod 'ESNetworkManager/Promise'
pod 'ESNetworkManager/Rx'
pod 'ESNetworkManager/ObjectMapper'

您还需要确保您已经选择了使用框架

use_frameworks!

然后使用 CocoaPods 0.36 或更高版本运行 pod install

使用说明

初始化 ESNetworkRequest

let request = ESNetworkRequest(base: "https://sample.com", path: "api/path")
    request.parameters = [:]
    request.headers = [:]
    request.encoding = JSONEncoding.default
    request.method = .post
    request.selections = []
    print(request)

ESNetworkManager 执行 ESNetworkRequest 并完成

func login(email: String, password: String) {
    let request = ESNetworkRequest(base: "https://sample.com", path: "api/login")
    request.parameters = ["email": email, "password": password]
    request.encoding = JSONEncoding.default
    request.method = .post
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<User>) in
        guard case .success(let user) = response else {
            return
        }
        print(user.name)
    }
}

上传多部分文件

func upload(avatar: UIImage) {
    let data = avatar.jpegData(compressionQuality: 0.5)!
    let file = MPFile(data: data, key: "file", name: "image.jpeg", memType: "image/jpeg")
    let request = ESNetworkRequest(base: "https://sample.com", path: "api/upload")
    request.parameters = [:]
    request.encoding = JSONEncoding.default
    request.method = .post
    ESNetworkManager.upload(data: .multipart([file]), request: request, progress: { progress in
        print(progress.frationCompleted)
    }) { (response: ESNetworkResponse<String>) in
         guard case .success(let imageUrlString) = response else {
                  return
              }
              print(imageUrlString)
    }
}

下载文件

func downloadFile() {
    let request = ESNetworkRequest(base: "https://sample.com", path: "api/file.mp4")
    ESNetworkManager.download(request: request, progress: { (progress) in
        print(progress.fractionCompleted)
    }) { (response) in
        guard case .success(let url) = response else {
            return
        }
        print(url.absoluteString)
    }
}

会话管理器

覆盖会话管理器

class NetworkManager: ESNetworkManager {
    static let session: Session = {
        let manager = ServerTrustManager(evaluators: ["serverurl.com": DisabledEvaluator()])
        let configuration = URLSessionConfiguration.af.default
        return Session(configuration: configuration, serverTrustManager: manager)
    }()
    
    override class var Manager: Session {
        return session
    }
}

数据响应映射

如果你有一个带有预定义错误或错误服务器消息的基本响应,如以下登录响应情况,则使用高级数据响应映射

let json_success = """
    {
    "status": 200,
    "message": "Welcom",
    "content": {"token": "2e13eer1rt13tvxcvz"}
    }
"""
let json_fail = """
    {
    "status": 1002,
    "message": "Wrong Email or password"
    }
    """

简单覆盖数据响应映射

class NetworkManager: ESNetworkManager {
    override class func map(_ response: AFDataResponse<Any>) -> ESNetworkResponse<JSON> {
        if let error = response.error {
            return .failure(error)
        }
        let json = JSON(response.value)
        let status: Int = json.status.value() ?? 0
        switch status {
        case 200:
            return .success(json.content)
        case let code:
            return .failure(NSError.init(error: json.message.value() ?? "", code: code))
        }
    }
}

// Example For Wrong Email or password
login(email: "email", password: "password") { (response) in
   guard case .error(let error) = response else {
           return
   }
   print(error.localizedDescription) // --> Wrong Email or password
   print(error.statusCode) // --> 1002
}

JSON

JSON是一个枚举,其用法类似于JavaScript对象而不是字典,例如

let dictionary: [String: Any] = ["name": "Demo User",
                                 "age": 41,
                                 "type": 1,
                                 "verified": 0,
                                 "activated": true,
                                 "phones": ["134234", "532412"],
                                 "adddress": ["title": "Cairo", "latitude": "12.23123", "logintude": "41.12323"],
                                 "family": [["name": "Demo Son", "age": 19, "activated": false]]]

let json = JSON(dictionary)

json.name.value() // --> Demo User
json.phones.0.value() // --> 134234
json.adddress.title.value() // --> Cairo
json.family.0.name.value() // -->  Demo Son

PromiseKit 扩展

import PromiseKit
func login(email: String, password: String) -> Promise<String>{
    return ESNetworkManager.execute(request: request)
}
func getProfile(token: String) -> Promise<User>{
    return ESNetworkManager.execute(request: request)
}
// Example
func login() {
    firstly {
        login(email: "[email protected]", password: "password")
    }.then {
        getProfile(token: $0)
    }.done { (user) in
        print(user.name)
    }.catch { (error) in
        print(error.localizedDescription)
    }
}

RxSwift 扩展

import RxSwift
 func login(email: String, password: String) -> Single<String>{
     return ESNetworkManager.execute(request: request)
 }
 func getProfile(token: String) -> Single<User>{
     return ESNetworkManager.execute(request: request)
 }
 
// Example
func login() {
    login(email: "[email protected]", password: "password")
    .flatMap({getProfile(token: $0)})
        .subscribe(onSuccess: { (user) in
            print(user.name)
        }) { (error) in
            print(error.localizedDescription)
    }.disposed(by: disposeBag)
}

请求选择

用于选择特定响应的情况,例如,用户成功登录后返回的 JSON 响应

let response  = """
{
    "staus": 200
    "messase" : "Success"
    "content": {
        "id": 12,
        "name": "Demo User",
        "email": "[email protected]",
        "token": "Token"
        "phones": ["01000000000", "02000000000"]
    }
}
"""

不需要创建类似这样的模型

class Model {
    var status: Int?
    var messase: Int?
    var content: User?
}

只需设置请求选择(键或索引),例如

func login(email: String, password: String) {
    request.selection = [.key("content")]
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<User>) in
        guard case .success(let user) = response else {
            return
        }
        print(user.name)
    }
}

或者

func login(email: String, password: String) {
    request.selection = [.key("content"), .key("token")]
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<String>) in
        guard case .success(let token) = response else {
            return
        }
        print(token) // --> Token
    }
}

或者

func login(email: String, password: String) {
    request.selection = [.key("content"), .key("phones"), .index(0)]
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<String>) in
        guard case .success(let phone) = response else {
            return
        }
        print(phone) // --> 01000000000
    }
}

响应映射

  • 为 Codable、ObjectMapper 和 RawRepresentable 实现了默认映射

  • 在这种情况下,API 响应

let dictionary: [String: Any] = ["name": "Demo User",
                                 "age": 41,
                                 "type": 1,
                                 "verified": 0,
                                 "activated": true,
                                 "phones": ["134234", "532412"],
                                 "adddress": ["title": "Cairo", "latitude": "12.23123", "logintude": "41.12323"],
                                 "family": [["name": "Demo Son", "age": 19, "activated": false]]]

Codable

Codable

func login(email: String, password: String) {
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<User>) in
        guard case .success(let user) = response else {
            return
        }
        print(user.name) // --> Demo User
    }
}

class User: Codable {
    var name = ""
    var age = ""
    var activated = false
    private enum CodingKeys: String, CodingKey {
        case name, age, activated
    }
}

字符串

func login(email: String, password: String) {
    request.selection = [.key("name")]
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<String>) in
        guard case .success(let name) = response else {
            return
        }
        print(name) // --> Demo User
    }
}

数组

func login(email: String, password: String) {
    request.selection = [.key("phones")]
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<[String]>) in
        guard case .success(let phones) = response else {
            return
        }
        print(phones) // --> ["134234", "532412"]
    }
}

ObjectMapper

func login(email: String, password: String) {
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<User>) in
        guard case .success(let user) = response else {
            return
        }
        print(user.name) // --> Demo User
    }
}
class User: Mappable {
    var name = ""
    var age = ""
    var activated = false
    required init?(map: Map) {}
    func mapping(map: Map) {
        name <- "name"
        age <- "age"
        activated <- "activated"
    }
}

RawRepresentable

func login(email: String, password: String) {
    request.selection = [.key("type")]
    ESNetworkManager.execute(request: request) { (response: ESNetworkResponse<UserType>) in
        guard case .success(let type) = response else {
            return
        }
        print(type) // --> UserType.admin
    }
}

enum UserType: Int {
    case admin = 1
}

其他映射

// Mapping Protocol
protocol MappingTool {
    init(json: [String: Any])
}

// SubClassing from ESNetworkResponseMapper<T>
class MappingToolNetworkResponseMapper<T>: ESNetworkResponseMapper<T> where T: MappingTool {
    override func map(_ response: ESNetworkResponse<JSON>, selections: [Selection]) -> ESNetworkResponse<T> {
        guard case .success(var value) = response else {
            return .failure(response.error!)
        }
        value = value[selections]
        guard let json = value.object as? [String: Any] else {
            return .failure(NSError.init(error: "Unable to get json from \(value)", code: -1))
            
        }
        return .success(T.init(json: json))
    }
}

func login(email: String, password: String) {
    ESNetworkManager.execute(request: request,
                             mapper: MappingToolNetworkResponseMapper()) { (response: ESNetworkResponse<MUser>) in
        guard case .success(let user) = response else {
            return
        }
        print(user.name!) // --> Demo User
    }
}

// Declare a class
class MUser: MappingTool {
    var name: String?
    var age: Int?
    required init(json: [String : Any]) {
        name = json["name"] as? String
        age = json["age"] as? Int
    }
}

许可证

ESNetworkManager遵循MIT许可证发布。 查看LICENSE获取详细信息。