一凄诞、基本介紹
1. 什么是 Moya
(1)我們知道在 iOS 開發(fā)中,可以使用 URLSession 進行網(wǎng)絡(luò)請求泼差。但為了方便起見椭盏,我通常會選擇使用 Alamofire 這樣的第三方庫。這些庫本質(zhì)上也是基于 URLSession 的,但其封裝了許多細節(jié)利职,可以讓我們網(wǎng)絡(luò)請求相關(guān)代碼(如獲取數(shù)據(jù)趣效,提交數(shù)據(jù),上傳文件猪贪,下載文件等)更加簡潔易用跷敬。
(2)而 Moya 又是一個基于 Alamofire 的更高層網(wǎng)絡(luò)請求封裝抽象層。Moya 也就可以看做我們的網(wǎng)絡(luò)管理層热押,用來封裝 URL西傀、參數(shù)等請求所需要的一些基本信息。使用后我們的客戶端代碼會直接操作 Moya桶癣,然后 Moya 去管理請求拥褂,而不用跟 Alamofire 進行直接接觸。
- GitHub 主頁地址:https://github.com/Moya/Moya
2牙寞,使用 Moya 的優(yōu)點
(1)在我們項目的 Service饺鹃、View、或者 Model 文件中可能都會出現(xiàn)請求網(wǎng)絡(luò)數(shù)據(jù)的情況间雀,如果直接使用 Alamofire悔详,不僅很繁瑣,而且還會使代碼變得很混亂惹挟。
(2)過去我們通常的做法是在項目中添加一個網(wǎng)絡(luò)請求層(比如叫做 APIManager茄螃、或者 NetworkModel),用來管理網(wǎng)絡(luò)請求匪煌。但這樣做可能會遇到一些問題:
- 難以開發(fā)一個新的 App(不知從哪里下手)
- 難以維護現(xiàn)有的 App(這一層比較混亂责蝠,混合了各種請求不好管理。)
-
難以做做單元測試
image.png
二萎庭、安裝配置
由于 Moya 需要依賴 Alamofire 庫霜医,手動配置會麻煩些。所以下面我們還是使用 CocoaPods 來進行安裝配置驳规。
1肴敛,創(chuàng)建 Podfile
首先進入到工程的根目錄下,創(chuàng)建空白的 Podfile 文件吗购。
cd /Users/hangge/Documents/Code/hangge_1797
touch Podfile
2医男,編輯 Podfile
我們在 Podfile 文件中寫上需要引入的第三方庫:Alamofire、Moya捻勉、SwiftyJSON(方便解析返回的 JSON 數(shù)據(jù))
use_frameworks!
def libraries
pod 'Alamofire'
pod 'Moya'
pod 'SwiftyJSON'
end
target 'hangge_1797' do
platform :ios, '8.0'
libraries
end
3镀梭,開始導(dǎo)入庫
執(zhí)行下面命令,開始導(dǎo)入前面配置的第三方庫踱启。
cd /Users/hangge/Documents/Code/hangge_1358
pod install
4报账,打開新生成的 .xcworkspace 文件
往后我們就需要使用這個新生成的 hangge_1797.xcworkspace 文件來開發(fā)研底。因為原來的工程(hangge_1358.xcodeproj)設(shè)置已經(jīng)被更改了,如果我們直接打開原來的工程文件去編譯就會報錯透罢。
三榜晦、使用樣例
1,效果圖
(1)我們使用 Moya 調(diào)用豆瓣 FM 的 API 接口羽圃,獲取所有的頻道列表并顯示在表格中乾胶。
(2)點擊任意一個頻道,調(diào)用另一個接口隨機獲取該頻道下的一首歌曲朽寞,并彈出顯示识窿。
2,樣例代碼
(1)DouBanAPI.swift(網(wǎng)絡(luò)請求層)
- 首先我們定義一個 provider愁憔,即請求發(fā)起對象腕扶。往后我們?nèi)绻l(fā)起網(wǎng)絡(luò)請求就使用這個 provider。
- 接著聲明一個 enum 來對請求進行明確分類吨掌,這里我們定義兩個枚舉值分別表示獲取頻道列表半抱、獲取歌曲信息。
- 最后讓這個 enum 實現(xiàn) TargetType 協(xié)議膜宋,在這里面定義我們各個請求的 url窿侈、參數(shù)、header 等信息秋茫。
import Foundation
import Moya
//初始化豆瓣FM請求的provider
let DouBanProvider = MoyaProvider<DouBan>()
/** 下面定義豆瓣FM請求的endpoints(供provider使用)**/
//請求分類
public enum DouBan {
case channels //獲取頻道列表
case playlist(String) //獲取歌曲
}
//請求配置
extension DouBan: TargetType {
//服務(wù)器地址
public var baseURL: URL {
switch self {
case .channels:
return URL(string: "https://www.douban.com")!
case .playlist(_):
return URL(string: "https://douban.fm")!
}
}
//各個請求的具體路徑
public var path: String {
switch self {
case .channels:
return "/j/app/radio/channels"
case .playlist(_):
return "/j/mine/playlist"
}
}
//請求類型
public var method: Moya.Method {
return .get
}
//請求任務(wù)事件(這里附帶上參數(shù))
public var task: Task {
switch self {
case .playlist(let channel):
var params: [String: Any] = [:]
params["channel"] = channel
params["type"] = "n"
params["from"] = "mainsite"
return .requestParameters(parameters: params,
encoding: URLEncoding.default)
default:
return .requestPlain
}
}
//是否執(zhí)行Alamofire驗證
public var validate: Bool {
return false
}
//這個就是做單元測試模擬的數(shù)據(jù)史简,只會在單元測試文件中有作用
public var sampleData: Data {
return "{}".data(using: String.Encoding.utf8)!
}
//請求頭
public var headers: [String: String]? {
return nil
}
}
(2)ViewController.swift
(主視圖代碼)
代碼中高亮部分是通過 Moya 發(fā)起網(wǎng)絡(luò)請求「刂可以看到頁面上不再有 url 地址圆兵、參數(shù)拼接、請求方式等枢贿,比直接使用 Alamofire 清爽許多殉农。
import UIKit
import SwiftyJSON
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
//顯示頻道列表的tableView
var tableView:UITableView!
//頻道列表數(shù)據(jù)
var channels:Array<JSON> = []
override func viewDidLoad() {
super.viewDidLoad()
//創(chuàng)建表視圖
self.tableView = UITableView(frame:self.view.frame, style:.plain)
self.tableView!.delegate = self
self.tableView!.dataSource = self
//創(chuàng)建一個重用的單元格
self.tableView!.register(UITableViewCell.self,
forCellReuseIdentifier: "SwiftCell")
self.view.addSubview(self.tableView!)
//使用我們的provider進行網(wǎng)絡(luò)請求(獲取頻道列表數(shù)據(jù))
DouBanProvider.request(.channels) { result in
if case let .success(response) = result {
//解析數(shù)據(jù)
let data = try? response.mapJSON()
let json = JSON(data!)
self.channels = json["channels"].arrayValue
//刷新表格數(shù)據(jù)
DispatchQueue.main.async{
self.tableView.reloadData()
}
}
}
}
//返回表格分區(qū)數(shù)
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
//返回表格行數(shù)(也就是返回控件數(shù))
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return channels.count
}
//創(chuàng)建各單元顯示內(nèi)容(創(chuàng)建參數(shù)indexPath指定的單元)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
-> UITableViewCell {
//為了提供表格顯示性能,已創(chuàng)建完成的單元需重復(fù)使用
let identify:String = "SwiftCell"
let cell = tableView.dequeueReusableCell(
withIdentifier: identify, for: indexPath)
cell.accessoryType = .disclosureIndicator
//設(shè)置單元格內(nèi)容
cell.textLabel?.text = channels[indexPath.row]["name"].stringValue
return cell
}
//處理列表項的選中事件
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
//獲取選中項信息
let channelName = channels[indexPath.row]["name"].stringValue
let channelId = channels[indexPath.row]["channel_id"].stringValue
//使用我們的provider進行網(wǎng)絡(luò)請求(根據(jù)頻道ID獲取下面的歌曲)
DouBanProvider.request(.playlist(channelId)) { result in
if case let .success(response) = result {
//解析數(shù)據(jù)局荚,獲取歌曲信息
let data = try? response.mapJSON()
let json = JSON(data!)
let music = json["song"].arrayValue[0]
let artist = music["artist"].stringValue
let title = music["title"].stringValue
let message = "歌手:\(artist)\n歌曲:\(title)"
//將歌曲信息彈出顯示
self.showAlert(title: channelName, message: message)
}
}
}
//顯示消息
func showAlert(title:String, message:String){
let alertController = UIAlertController(title: title,
message: message, preferredStyle: .alert)
let cancelAction = UIAlertAction(title: "確定", style: .cancel, handler: nil)
alertController.addAction(cancelAction)
self.present(alertController, animated: true, completion: nil)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}