SwiftyRestKit
一种简单、面向协议的方法,简化 iOS 上的 REST 请求。
这个库结合了网络上的几个不同的想法,集成到一个可直接使用的库中。
安装
Cocoapods
pod 'SwiftyRestKit'
手动
此项目没有依赖项,因此,如果需要,您可以仅下载并将 Swift 文件添加到您的项目中。
如何使用
首先,为API创建一个枚举,或者如果你的API有很多不同的端点,为你创建一个端点枚举。我们将使用TMDb的API,并在此示例中创建搜索和趋势电影的请求。
enum TMDbAPI {
case trendingMovies(page: Int)
case searchMovies(searchTerm: String, page: Int)
}
然后,创建一个扩展并采用EndpointType协议。
extension TMDbAPI: EndPointType {
}
现在,让我们遵守这个协议。
extension TMDbAPI: EndPointType {
var apiClientKey: String? {
return "Your API Key"
}
var apiClientSecret: String? {
return nil
}
var baseURLString : String {
return "https://api.themoviedb.org/3/"
}
var baseURL: URL {
guard let url = URL(string: baseURLString) else {
fatalError("baseURL could not be configured.")}
return url
}
var path: String {
switch self {
case .trendingMovies:
return "trending/movie/day"
case .searchMovies:
return "search/movie"
}
}
var httpMethod: HTTPMethod {
switch self {
default:
return .get
}
}
var task: HTTPTask {
switch self {
case .trendingMovies(let page):
let urlParameters: Parameters = ["language" : defaultLanguage, "api_key" : apiClientKey!, "page" : "\(page)"]
return .requestWith(bodyParameters: nil, urlParameters: urlParameters)
case .searchMovies(let searchTerm, let page):
let urlParameters: Parameters = ["language" : defaultLanguage, "api_key" : apiClientKey!, "query" : searchTerm, "page" : "\(page)"]
return .requestWith(bodyParameters: nil, urlParameters: urlParameters)
}
}
var headers: HTTPHeader? {
switch self {
default:
return nil
}
}
}
那么,这里有什么呢
- apiClientKey 和 apiClientSecret:根据你访问的API,你可能需要设置其中一个或两个,如果不是,则直接返回nil
- baseURLString 和 baseURL:很直观,就像示例中那样设置。
- path:返回枚举中每个用例的路径。
- httpMethod:返回方法,支持.get、.post、.put、.patch和.delete。
- task:设置你的URL或body参数并返回
- .request(无参数)
- .requestWith(bodyParameters: bodyParameters, urlParameters: urlParameters) 如果你有其中一个或两个
- .requestWithHeaders(bodyParameters: bodyParameters, urlParameters: urlParameters, additionalHeaders: headers) 如果你需要在你的头中传递一些内容。请注意,你应该在此处传递你的headers变量中的“headers”值。
- headers:为每个用例设置任何额外的头或nil。
你的端点准备好了,现在,让我们创建一个服务。为你的服务创建一个结构体并扩展它以采用Service协议。然后设置你的端点。
struct SearchService {
}
extension SearchService: Service {
typealias EndPoint = TMDbAPI
}
添加另一个扩展,并采用以下协议之一:Gettable、Createable、Updateable和Deleteable。在这种情况下,因为我们只有获取某些方法,所以我们将只采用Gettable。
extension SearchService: Gettable { }
当你采用Service协议并设置其EndPoint时,你会得到一个网络管理器,它本身有一个调度器,可以执行从你的端点发出的请求。还有一个Result类型,如果你请求成功,你可以用它返回一个对象,或者在失败的情况下返回SwiftyRestKitError。这里看起来是这样的
struct SearchService {
func fetchTrendingMovies(page: Int, completion: @escaping (Result<MovieCollection>) -> Void) {
manager.dispatcher.request(.trendingMovies(page: page)) { (data, response, error) in
let result = self.processResult(data, response, error)
completion(result)
}
}
func searchMoviesByTitle(searchTerm: String, page: Int, completion: @escaping (Result<MovieCollection>) -> Void) {
manager.dispatcher.request(.searchMovies(searchTerm: searchTerm, page: page)) { (data, response, error) in
let result = self.processResult(data, response, error)
completion(result)
}
}
private func processResult(_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Result<MovieCollection> {
if let error = error {
return Result.Failure(error)
}
guard let response = response as? HTTPURLResponse else {
return Result.Failure(SwiftyRestKitError.decodingFailed)
}
let result = self.manager.handleNetworkResponse(response)
switch result {
case .Success:
let decoder = JSONDecoder()
guard let responseData = data, let movieCollection = try? decoder.decode(MovieCollection.self, from: responseData) else {
return Result.Failure(InternalError.decodingError)
}
return Result.Success(movieCollection)
case .Failure(let error):
print(error.localizedDescription)
return Result.Failure(SwiftyRestKitError.lostConnection)
}
}
}
最后,在你的视图控制器中,创建一个方法来注入服务作为依赖项并获取数据。
class TrendingMoviesViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
fetchTrendingMovies(fromService: SearchService())
}
func fetchTrendingMovies<S: Gettable>(fromService service: S) {
guard let service = service as? SearchService else {
return
}
service.fetchTrendingMovies(page: 1) { (result) in
DispatchQueue.main.async { [unowned self] in
switch result {
case .Success(let movieCollection):
// Show results
case .Failure(let error):
// Show alert for error
}
}
}
}
...
}