swift 网络请求

使用moya框架

pod 'Moya', '~> 15.0'
# handyjson 处理数据

pod 'HandyJSON',    '5.0.3-beta'

定义一个BaseAPI


import Moya
/// 此参数应该放在项目统一宏定义中, 根据测试环境, 预上线, 正式环境判断
private var BASEURL = "www.baidu.com"

///网络请求基类, 基类参数基本固定
class BaseAPI {
    ///请求域名
    let requestBaseUrl = BASEURL
    /// 请求公参,token啥的
    var requestCommenParam: [String: Any] {
        get {
            return [:]
        }
    }
    ///请求 header 
    var requestHeader: [String: String]? {
        get {
            return ["Content-Type": "text/plain", "charset": "utf-8"]
        }
    }
    ///请求超时时间
    var requestTimeOut = 30.0
    
}

定义自己的项目api继承baseApi

class DemoAPI: BaseAPI {
    ///请求路径
    var requestPath: String?
    ///请求形参
    var requestParam: [String: Any]?
    ///请求方法, 默认 get
    var requestMethod: Moya.Method = .get
}
extension DemoAPI : TargetType {
    ///域名
    var baseURL: URL { return URL(string: requestBaseUrl)! }
    ///路径
    var path: String {
        guard let requestPath = requestPath else { return "" }
        return requestPath
    }
    ///请求方法
    var method: Moya.Method {
        return requestMethod
    }
    ///请求任务, 在此合并公参
    var task: Task {
        // 合并请求参数与公参
        var param: [String: Any] = [:]
        // 先合并公参
        param.merge(requestCommenParam) { return $1 }
        // 再合并形参, 如果形参中也有公参相同参数, 会覆盖掉公参, 以形参为主
        if let requestParam = requestParam {
            param.merge(requestParam) { return $1 }
        }
        return .requestParameters(parameters: param, encoding: URLEncoding.default)
        
    }
    /// header
    var headers: [String : String]? {
        return requestHeader
    }
    
}

定义一个请求管理器

import Moya
import HandyJSON
 
///管理所有模块, 私有方法, 子请求通过 extension 实现
class DemoRequestManager {
    
    
    /// 所有模块均通过此方法请求数据
    /// - **Parameters**:
    ///  - path: 路径
    ///  - method: method
    ///  - param: 参数
    ///  - done: 请求结果, 成功或者失败都通过此闭包回调
    static func DemoRequestModule<T: HandyJSON>(path: String, method: Moya.Method = .get, param: [String: Any]?, done: @escaping ((_ succeed: Bool, _ data: T?) -> Void)) {
        //API
        let api = RosettaAPI()
        api.requestPath = path
        api.requestParam = param
        api.requestMethod = method
        //设置超时
        let timeoutClosure = { (endpoint: Endpoint, done: @escaping MoyaProvider<RosettaAPI>.RequestResultClosure) in
            do {
                var request = try endpoint.urlRequest()
                request.timeoutInterval = api.requestTimeOut
                done(.success(request))
            } catch {
                return
            }
        }
        let provider = MoyaProvider<RosettaAPI>(requestClosure: timeoutClosure)
        
        provider.request(api) { result in
            //处理结果
            self.handleResult(result: result, done:done)
        }
    }
    
    /// 响应数据处理
    private static func handleResult<T: HandyJSON>(result:Result<Moya.Response, MoyaError>, done: @escaping ((_ succeed: Bool, _ data: T?) -> Void)){
        switch result {
        case .success(let response):
            let dic = try? response.mapJSON() as? Dictionary<String, Any>
            let value = T.deserialize(from: dic)
            //过滤接口成功状态
            if let result = T.deserialize(from: dic) {
                //回调到数据层
                done(true, result)
                
            } else {
                //打印请求错误信息
                done(false, nil)
            }
            // 打印请求信息
            if let request = response.request,
               let method = request.method,
               let json = String(data: response.data, encoding: .utf8){
                print("~~~~~~~~~~~Request Start~~~~~~~~~~~~")
                print("Method:\(method.rawValue)\nURL:\(request)\nBody:\(String(describing: request.httpBody))\nResult:\n\(json)")
                print("~~~~~~~~~~~Request End~~~~~~~~~~~~")
            }
        case .failure(let error):
            //网络错误
            print(error)
            done(false, nil)
        }
    }
    
}

比如请求一个banner

enum HomeApi: String {
    case banner = "/cms/banner" // banner图接口
}


/// 多人协助开发时, 开发者在自己的模块里创建 extension 即可, 编写自己模块所需要的接口
extension DemoRequestManager {
    //请求banner数据
    static func getBanner(param: [String: Any]?, done: @escaping (_ succeed: Bool ,_ data: BannerModels?) -> Void) {
        DemoRequestModule(path: HomeApi.banner.rawValue, method: .get, param: param, done: done)
    }
}


# model 代码
import HandyJSON

struct BannerModel : HandyJSON {
    var id: Int?
    var name: String?
    var imagePath: String?
}

struct BannerModels :HandyJSON {
    var obj: [BannerModel]?
    var success: Bool?
    var msg:String?
    var code: String?
    var version:String?
    var size: Int?
}

swift webscoket链接

private let urlSession = URLSession(configuration: .default)
private var webSocketTask: URLSessionWebSocketTask?
private let baseURL = URL(string: BaseWsApi.binance.rawValue)!


// 连接socket
func connect() {
    //        do {
    stop()
    webSocketTask = urlSession.webSocketTask(with: baseURL)
    webSocketTask?.resume()
    receiveMessage()
    //        } catch {
    //            self.getTradeCard()
    //        }
}
    
func stop() {
    webSocketTask?.cancel(with: .goingAway, reason: nil)
}
    
// 发送消息
private func sendMessage(symbolPair:String)
{
    //{"method":"SUBSCRIBE","params":["btcusdt@ticker"],"id":1}
    var subTicker:TickerSubModel = TickerSubModel()
    subTicker.params?.append(symbolPair + "@ticker")
    do {
        let jsonData = try JSONEncoder().encode(subTicker)
        let subString = String(decoding: jsonData, as: UTF8.self)
        print(subString)
        let message = URLSessionWebSocketTask.Message.string(subString)
        webSocketTask?.send(message) { error in
            if let error = error {
                print("WebSocket couldn’t send message because: \(error)")
            }
        }
        
    } catch {
        print(error.localizedDescription)
    }
}
    
// 接收消息处理
private func receiveMessage() {
    webSocketTask?.receive {[weak self] result in
        switch result {
        case .failure(let error):
            print("Error in receiving message: \(error)")
        case .success(.string(let str)):
            do {
                //                    let json = String(data: Data(str.utf8), encoding: .utf8)
                let bmodel = try JSONDecoder().decode(BinanceTickerModel.self, from: Data(str.utf8))
                DispatchQueue.main.async{
                    self?.addMarket(bmodel: bmodel)
                }
                
            } catch  {
                print("error is \(error.localizedDescription)")
            }
            
            self?.receiveMessage()
            
        default:
            print("default")
        }
    }
    }

swift 原生codeable 解析到不存在的值 为nil,想要赋默认值时需要扩展jsondecoder

extension KeyedDecodingContainer {
    // bool
    public func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? {
        if let value = try? decode(type, forKey: key) {
            return value
        }
        return false
    }
    
    // int
    public func decodeIfPresent(_ type: Int.Type, forKey key: K) throws -> Int? {
        if let value = try? decode(type, forKey: key) {
            return value
        }
        return 0
    }
    
    // string
    public func decodeIfPresent(_ type: String.Type, forKey key: K) throws -> String? {
        if let value = try? decode(type, forKey: key) {
            return value
        }
        return ""
    }
}

swift moya async + await 请求


上次更新:
贡献者: liutian