Moya 简单使用

Moya

用于将 接口请求数据 转换为对象

1
2
3
4
5
6
7
8
9
10
11
12
13
struct SYCUser: Codable{
let id: Int
let userName: String
let avatar: String
let accessToken: String

private enum CodingKeys: String, CodingKey {
case id
case userName = "user_name"
case avatar
case accessToken = "access_token"
}
}

接口定义 (EndPoint: TargetType)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135

import Foundation
import Moya

/// 接口
enum SYCEndPoint {
/// 登录
case login(String)
/// 获取 socket 配置
case socket
/// 注销
case logout(String)
/// 上传文件
case upload(fileUrl: URL, fileName: String)
case uploadMultipart(fileUrls: [URL], uploadType: String, fileCount: Int)
/// 下载
case download(filePath: String)
case downloadSaveName(filePath: String, saveName: String)
}

extension SYCEndPoint: TargetType{
/// 服务器地址
var baseURL: URL {
return URL(string: "base")!
}


/// 接口地址
var path: String {
switch self {
case .login:
return "login"
case .logout:
return "logout"
case .socket:
return "socket"
case let .upload(_, fileName):
return "upload" + fileName
case .uploadMultipart:
return "upload multipart"
default:
return "undefine"
}
}

/// 请求方式
var method: Moya.Method {
switch self {
case .login, .logout:
return .post

default:
return .get
}
}

/// 测试数据
var sampleData: Data {
return "".data(using: String.Encoding.utf8)!
}

/// 发起请求
var task: Task {
switch self {
case .login(let user):
var params = [String: Any]()
params["user"] = user
return .requestParameters(parameters: params, encoding: URLEncoding.default)

case .logout(let user):
var params = [String: Any]()
params["user"] = user
return .requestParameters(parameters: params, encoding: URLEncoding.default)

case let .upload(fileUrl, _):
return .uploadFile(fileUrl)

case let .uploadMultipart(fileUrls, uploadType, fileCount):

// url 参数
let urlParam = ["param1": uploadType]

// request body 参数
let intData = String(fileCount).data(using: .utf8)!
let intFormat = MultipartFormData(provider: .data(intData), name: "param2")

let firstFile = MultipartFormData(provider: MultipartFormData.FormDataProvider.file(fileUrls.first!), name: "file1", fileName: "firstname.png", mimeType: "image/png")

let lastFile = MultipartFormData(provider: .file(fileUrls.last!), name: "file2", fileName: "lastname.png", mimeType: "image/png")

//.uploadMultipart([intFormat, firstFile, lastFile])
return .uploadCompositeMultipart([intFormat, firstFile, lastFile], urlParameters: urlParam)

case .download:
return .downloadDestination(defaultDownloadDestination)

case let .downloadSaveName(_, saveName):
let savePath = defaultDownloadPath.appendingPathComponent(saveName)
let downLoadDestination: DownloadDestination = { _, _ in
// 覆盖同名
return (savePath, [.removePreviousFile])
}
return .downloadDestination(downLoadDestination)

default:
return .requestPlain
}
}

var headers: [String : String]? {
return nil
}


}

let defaultDownloadPath: URL = {
let documents = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask)
if #available(iOS 10.0, *) {
return documents.first ?? FileManager.default.temporaryDirectory
} else {
// Fallback on earlier versions
return documents.first!
}
}()

private let defaultDownloadDestination: DownloadDestination = { (temporaryURL: URL,response: HTTPURLResponse) in

// 不改变文件名, 同名不覆盖
//return (defaultDownloadPath.appendingPathComponent(response.suggestedFilename!), [])

// 不改变文件名, 同名覆盖
return (defaultDownloadPath.appendingPathComponent(response.suggestedFilename!), [.removePreviousFile])
}

接口请求 MoyaProvider

  • 登录登出 普通请求
  • 上传
  • 下载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165

import Foundation
import Moya
import Result

struct SYCProvider{
static let provider = MoyaProvider<SYCEndPoint>()

/// 网络请求
///
/// - Parameters:
/// - endpoint: 接口
/// - success: 成功回调
/// - error: 错误码
/// - failure: 失败回调
static func request(
endpoint: SYCEndPoint,
success: @escaping (Any) -> Void,
error: @escaping (Int) -> Void,
failure: @escaping (MoyaError) -> Void) -> Void{

provider.request(endpoint) { (result: Result<Response, MoyaError>) in
switch result{
case let .success(response):
do {
// 成功
_ = try response.filterSuccessfulStatusCodes()
let json = try response.mapJSON()
success(json)
}catch let err {
// 失败
print("错误原因:\(err.localizedDescription)")
let statusCode = (err as! MoyaError).response!.statusCode
error(statusCode)
}
case let .failure(error):
failure(error)
}
}
}

static func upload(){

}



// MARK: test
/// 下载单个文件
func testDownload(){
let filePtah = "xx/xx"
let endpoint = SYCEndPoint.download(filePath: filePtah)
SYCProvider.provider.request(endpoint, callbackQueue: nil, progress: { (progress: ProgressResponse) in
//实时打印出下载进度
print("当前进度: \(progress.progress)")
}) { (result: Result<Response, MoyaError>) in
if case .success = result{
let file = defaultDownloadPath.appendingPathComponent(filePtah)
print(file)
}
}
}
/// 下载文件保存自定义文件名
func testDo(){
let fileName = "file"
let filePath = "file/path"
let endpoint = SYCEndPoint.downloadSaveName(filePath: filePath, saveName: fileName)
SYCProvider.provider.request(endpoint, callbackQueue: nil, progress: { (progress: ProgressResponse) in
//实时打印出下载进度
print("当前进度: \(progress.progress)")
}) { (result: Result<Response, MoyaError>) in
if case .success = result{
let file = defaultDownloadPath.appendingPathComponent(fileName)
print(file)
}
}
}
/// 上传文件
func testUpload(){
let file = URL(fileURLWithPath: "file/path")
let endpoint = SYCEndPoint.upload(fileUrl: file, fileName: "file-name")
SYCProvider.provider.request(endpoint, callbackQueue: nil, progress: { (progress: ProgressResponse) in
//实时打印出上传进度
print("当前进度: \(progress.progress)")
}) { (result: Result<Response, MoyaError>) in
if case let .success(response) = result{
// 响应状态码:200, 401, 500...
let statusCode = response.statusCode
print(statusCode)
}
}
}

/// 上传多个文件
func testUploadMultipart(){
let firstUrl = URL(string: "xx/xx")!
let lastUrl = URL(string: "xx/xx")!
let endpoint = SYCEndPoint.uploadMultipart(fileUrls: [firstUrl, lastUrl], uploadType: "imageHaha", fileCount: 2)
SYCProvider.provider.request(endpoint, callbackQueue: nil, progress: { (progress: ProgressResponse) in
//实时打印出上传进度
print("当前进度: \(progress.progress)")
}) { (result: Result<Response, MoyaError>) in
if case let .success(response) = result{
// 响应状态码:200, 401, 500...
let statusCode = response.statusCode
print(statusCode)
}
}
}

/// 使用自己封装的 request
func testLogin1(){

let loginEndpoint = SYCEndPoint.login("user")
SYCProvider.request(endpoint: loginEndpoint, success: { (result) in
// success
}, error: { (statusCode) in
// code
}) { (err: MoyaError) in
// err
}
}
/// 使用 provider 的 request
func testLogin2(){
// 测试登录
let loginEndpoint = SYCEndPoint.login("user")

SYCProvider.provider.request(loginEndpoint) { (result: Result<Response, MoyaError>) in
switch result{
case .success(let response):
// 请求成功
// 响应状态码:200, 401, 500...
//let statusCode = response.statusCode
// 响应数据
//let data = response.data
//let json = try? response.mapJSON()
//let model = try JSONDecoder().decode(SYCUser.self, from: data)

do {
// 成功
_ = try response.filterSuccessfulStatusCodes()
_ = try response.mapJSON()
}catch let err {
// 失败
print("错误原因:\(err.localizedDescription)")
}

case .failure(let err):
// 请求失败
print("错误原因:\(err.errorDescription!)")
switch err{
case .imageMapping(let response):
print(response)
case .jsonMapping(let response):
print(response)
case .statusCode(let response):
print(response)
default:
break
}
break
}
}
}
}

多个 provider 合并为一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum FirstEndPoint: TargetType{
// ...
case login(u: String, p: String)
}
enum SecondEndPoint: TargetType{
// ...
}
// target
let otherProvider = MoyaProvider<FirstEndPoint>()
let cfProvider = MoyaProvider<SecondEndPoint>()
// multi target
let provider = MoyaProvider<MultiTarget>()
provider.request(MultiTarget(FirstEndPoint.login(u: "u", p: "p"))) { (result) in
// ...
}

插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
final class SYCProviderPlugin: PluginType {
private let viewController: UIViewController
private var activity: UIActivityIndicatorView!
let token: String

init(viewController: UIViewController, token: String) {
self.viewController = viewController
self.token = token
self.activity = UIActivityIndicatorView(style: UIActivityIndicatorView.Style.gray)
self.activity.center = self.viewController.view.center
}
// 准备请求
func prepare(_ request: URLRequest, target: TargetType) -> URLRequest {
var request = request
request.addValue(token, forHTTPHeaderField: "haha")
return request
}
// 开始请求
func willSend(_ request: RequestType, target: TargetType) {
self.viewController.view.addSubview(self.activity)
self.activity.startAnimating()
}
// 收到响应
func didReceive(_ result: Result<Response, MoyaError>, target: TargetType) {
self.activity.removeFromSuperview()
self.activity.stopAnimating()

guard case let Result.failure(error) = result else { return }
print(error.localizedDescription)
}
// 处理结果
func process(_ result: Result<Response, MoyaError>, target: TargetType) -> Result<Response, MoyaError> {

}
}

插件使用

1
2
3
let provider = MoyaProvider<SYCEndPoint>(plugins: [
SYCProviderPlugin(viewController: UIViewController(), token: "token")
])

本文标题:Moya 简单使用

文章作者:史彦超

发布时间:2018年11月14日 - 12:11

最后更新:2021年07月20日 - 16:07

原始链接:https://doingself.github.io/2018/11/14/Moya-%E7%AE%80%E5%8D%95%E4%BD%BF%E7%94%A8/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

Donate comment here