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 ""