SwiftyRestKit 1.0.4

SwiftyRestKit 1.0.4

Rodolfo Roca 维护。



  • 作者:
  • Rodolfo Roca

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

    ...

}