SimpleWebRequests
要求
- Swift 4
- iOS 10.3 及以上版本
示例项目
示例项目展示了如何使用库来实现处理网络调用的几种不同方式。只有当您想要能够轻松地在不同的数据源之间切换时,才需要自定义请求管理器。
安装
CocoaPods
SimpleWebRequests 可通过 CocoaPods 获取。要安装它,只需将以下行添加到您的 Podfile 中
pod 'SimpleWebRequests'
手册
将 'Sources' 目录中的所有文件添加到您的项目中,您就可以开始使用了。
入门
此库允许您快速搭建网络层,同时也支持更复杂的设置,涉及多个数据源(发布、开发、本地、模拟),但此处不做过多介绍。
要开始使用,您需要为每个网络请求添加 'DataResource' 的子类到项目中。我建议添加一个名为 '{ProjectName}DataResources.swift' 的文件,并将资源放置在此文件中,如下所示:
注意:标题、查询和资源参数通常不是静态的,将在资源实例化和使用时使用 'setHeaders:、' setBody: ' 和 ' setQuery: ' 设置。
// {ProjectName}ApiResources.swift
struct GetArticlesResource: DataResource {
typealias Model = [Article]
var urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default
var httpMethod: HttpMethod = .get
var headers: [String: String]?
var baseUrl: String = "https://www.test.com"
var methodPath: String? = "api/test"
var query: String?
var body: Data?
}
设置好资源后,您可以按照以下示例进行一些网络调用
let resource = GetArticlesResource()
let sessionTask = NetworkDataRequest(resource: resource).load { (response) in
switch response {
case let .success(model):
// Do something with the model coming back
case let .error(error):
// Handle general error
case let .httpError(statusCode):
// Handle HTTP error
}
}
如果您的请求需要一个体,可以直接在类定义中将资源的体设置,如果它们是固定的,或者如下所示
//let resource = AddArticlesResource()
let body = Article(title: "Test 1", description: "Testing article description.")
resource.setBody(body: JSONCoder.encode(object: body))
//let sessionTask = NetworkDataRequest(resource: resource).load { (response) in
//...
如果您的请求需要一个查询,可以直接在类定义中将资源的查询设置,如果它们是固定的,或者如下所示
//let resource = AddArticlesResource()
resource.setQuery(query: "?something=something")
//let sessionTask = NetworkDataRequest(resource: resource).load { (response) in
//...
如果您的请求需要标题,可以直接在类定义中将资源的标题设置,如果它们是固定的,或者如下所示
//let resource = AddArticlesResource()
resource.setHeaders(headers: ["SomeHeaderKey": "SomeHeaderValue"])
//let sessionTask = NetworkDataRequest(resource: resource).load { (response) in
//...
高级设置
高级设置允许您通过通用管理类在不同的数据源之间自动切换。为此设置,您需要继承以下结构体/协议。再次推荐用您的项目名作为类的前缀以提高清晰度。
protocol ApiInformation
protocol DataRequestManager
class MockDataRequestManager (optional)
class LocalDataRequestManager (optional)
让我们从 ApiInformation 类开始。这个类将允许您设置在运行时使用的所需数据源。在下面的示例中,我根据我的应用设置在构建配置中的调试或发布,在数据源之间进行切换。
// {ProjectName}ApiInformation.swift
struct {ProjectName}ApiInformation: ApiInformation {
#if DEBUG
var dataSource: DataSource = DataSource(type: .development, baseUrl: "http://development.something.com")
#else
var dataSource: DataSource = DataSource(type: .release, baseUrl: "https://release.something.com")
#endif
}
接下来要设置的是数据处理管理器,如下所示处理数据源切换
注意:在线数据请求管理器将被设置为 NetworkDataRequestManager 的新实例,除非您希望创建自己的继承自它的类来对该类进行所需的更改。
// {ProjectName}DataRequestManager.swift
public struct {ProjectName}DataRequestManager: DataRequestManager {
public typealias Resource = DataResource
public var apiInformation: ApiInformation = {ProjectName}ApiInformation()
public var onlineDataRequestManager: NetworkDataRequestManager = NetworkDataRequestManager()
public var localDataRequestManager: LocalDataRequestManager? = {ProjectName}LocalDataRequestManager()
public var mockDataRequestManager: MockDataRequestManager? = {ProjectName}MockDataRequestManager()
public static var shared: {ProjectName}DataRequestManager = {ProjectName}DataRequestManager()
}
模拟以及本地请求管理器的设置非常相似,这完全取决于您的实现和您希望如何处理这些数据请求,因为这个库只提供了这些部分的框架,以便数据处理管理器可以根据数据源进行切换。以下是每个可能看起来像的示例
模拟数据请求管理器
注意:在switch语句中,我展示了如何根据资源类型处理每种资源情况。请确保为每个case语句生成和返回相应的模拟数据。
// {ProjectName}MockDataRequest.swift
public class {ProjectName}MockDataRequestManager: MockDataRequestManager {
// All your custom variables to allow mocking your server
var articles: [Article] = [Article(title: "Mock 1", description: "Mocking an article."), Article(title: "Mock 2", description: "Mocking an article again.")]
public override func loadRequest<Resource: DataResource>(with resource: Resource, completion: @escaping (DataResponse<Resource.Model>) -> Void) -> URLSessionTask {
return handleRequest(with: resource, completion: completion)
}
}
extension {ProjectName}MockDataRequestManager {
// MARK: - Handle Request
func handleRequest<Resource: DataResource>(with resource: Resource, completion: @escaping (DataResponse<Resource.Model>) -> Void) -> URLSessionTask {
switch resource.self {
case is GetArticlesResource:
getArticles(completion: completion as! (DataResponse<GetArticlesResource.Model>) -> Void)
default:
return URLSessionTask()
}
return URLSessionTask()
}
}
extension {ProjectName}MockDataRequestManager {
// MARK: - Articles (GET)
func getArticles(completion: @escaping (DataResponse<GetArticlesResource.Model>) -> Void) {
let response: DataResponse = .success(model: articles)
completion(response)
}
}
本地数据请求管理器
注意:在switch语句中,我展示了如何根据资源类型处理每种资源情况。请确保从本地数据库返回适当的数据,例如,为每个case语句返回数据。
// {ProjectName}LocalDataRequest.swift
public class {ProjectName}LocalDataRequestManager: LocalDataRequestManager {
public override func loadRequest<Resource: DataResource>(with resource: Resource, completion: @escaping (DataResponse<Resource.Model>) -> Void) -> URLSessionTask {
// Get offline data
return handleRequest(with: resource, completion: completion)
}
}
extension {ProjectName}LocalDataRequestManager {
// MARK: - Handle Request
func handleRequest<Resource: DataResource>(with resource: Resource, completion: @escaping (DataResponse<Resource.Model>) -> Void) -> URLSessionTask {
switch resource.self {
case is GetArticlesResource:
let error = NSError(domain: "Local Data Fetch Error", code: 1, userInfo: nil)
let response: DataResponse = DataResponse<Resource.Model>.error(error: error)
completion(response)
default:
return URLSessionTask()
}
return URLSessionTask()
}
}
这是所需的所有高级设置。下面是如何使用高级设置处理调用的示例。
let resource = GetArticlesResource()
let sessionTask = {ProjectName}DataRequestManager.shared.loadRequest(with: resource) { (response) in
switch response {
case let .success(model):
// Do something with the model coming back
case let .error(error):
// Handle general error
case let .httpError(statusCode):
// Handle HTTP error
}
}
还有一件事情需要考虑,以帮助在调用这些请求时编写整洁的代码。考虑扩展数据请求管理器并添加表示每个请求所做的事情的函数和设置资源的设置,包括设置您的标头、主体和查询。以下是如何调用它的示例。
注意:我已经扩展了我的资源类,以根据某些参数生成查询参数。
// {ProjectName}DataRequestManager.swift
extension {ProjectName}DataRequestManager {
func getArticles(completion: @escaping (DataResponse<[Article]>) -> Void) -> URLSessionTask {
let resource = GetArticlesResource()
resource.setQuery(query: resource.articlesQuery())
return {ProjectName}DataRequestManager.shared.loadRequest(with: resource, completion: completion)
}
// Example including setting a body
func addArticle(with body: Article, completion: @escaping (DataResponse<Article>) -> Void) -> URLSessionTask {
let resource = AddArticlesResource()
resource.setBody(body: JSONCoder.encode(object: body))
return {ProjectName}DataRequestManager.shared.loadRequest(with: resource, completion: completion)
}
}
// {ProjectName}DataResources.swift
extension GetArticlesResource {
func articlesQuery(with id: Int) -> String {
return "?id=\(id)"
}
}
// SomeFile.swift
let sessionTask = {ProjectName}DataRequestManager.shared.getArticles { (response) in
switch response {
case let .success(model):
// Do something with the model coming back
case let .error(error):
// Handle general error
case let .httpError(statusCode):
// Handle HTTP error
}
}
作者
Niklas Fahl (fahlout) - LinkedIn
灵感来自:http://mrgott.com/swift-programing/33-rest-api-in-swift-4-using-urlsession-and-jsondecode
许可协议
SimpleWebRequests 在MIT许可协议下可用。有关更多信息,请参阅LICENSE文件。