Lotus

一个简单易用的网络请求库,针对小型不复杂的项目使用特别舒服😌。

  • 自定义缓存
  • 自定义Log
  • 自定义Debug
  • MD5加密
  • 自定义返回错误,自定义数据解析
  • 直接返回想要的Model

⚠️ 该项目虽然使用了Alamofire,但并非封装Alamofire,而是使用该库的编码,核心还是使用Apple提供的URLSession进行编写。

Dependence

Framework Usage
Alamofire 编码参数
SwiftyJSON 解析成SwiftJSON对象

Flow

Refrences

Send Flow

Receive Flow

Usage

Simple Usage

Make a request with URL

1
2
3
4
import Lotus

let url = URL(string: "https://httpbin.org/get")
Lotus.send(url)

Make a request with URLRequest

1
2
3
4
5
import Lotus

let url = URL(string: "https://httpbin.org/get")
let request = URLRequest(url: url)
Lotus.send(request)

Receive Response

ResultConversion define data to model.

⚠️ Only auto convert while using generic and genericArray call back.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct UserModel: ResultConversion {

let id: String
let name: String
let age: Int
let cover: URL?

init(json: JSON) {
id = json["id"].stringValue
name = json["name"].stringValue
age = json["age"].intValue
cover = json["cover"].url
}
/// Default is parse data from "data"
}
Receive Success Data
1
2
3
Lotus.send(router).receive(success: { data in
/// Receive response data with no error.
})
Receive Failed
1
2
3
Lotus.send(router).receive(failed: { error in
/// Receive response with error.
})
Receive progress
1
2
3
Lotus.send(router).receive(progress: { progress in
/// Receive response with progress.
})
Receive response
1
2
3
Lotus.send(router).receive(response: {
/// Receive response
})
Receive raw data
1
2
3
4
Lotus.send(router).receive(rawData: { data in
/// Receive response with raw data.
/// ⚠️ It may be called more than once
})
Receive Raw JSON
1
2
3
Lotus.send(router).receive(rawJSON: { json in
/// Receive response with json.
})

⚠️ Conflict with success call back.

Receive Generic

Model need to confirm ResultConversion

1
2
3
Lotus.send(router).receive(generic: { (model: UserModel) in
/// Receive response with custom generic.
})

⚠️ Conflict with success call back.

Receive Generic Array

Model need to confirm ResultConversion

1
2
3
Lotus.send(router).receive(generic: { (models: [UserModel]) in
/// Receive response with custom generic array.
})

⚠️ Conflict with success call back.

Suggesting Usage

Discussion

RequestConversion

RequestConversion define request conversion.
Request define request content.

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
/// Struct for request.
public struct Request {

/// Path for request.
public let path: String
/// Parameters for request.
public let parameters: Parameters?
/// Method for request.
public let method: Method
/// Encoding for request.
public let encoding: URLEncoding
/// HTTP Headers
public let headers: Headers?

/// Request struct
///
/// - Parameters:
/// - path: Path for request
/// - parameters: Parameters for request, default is `nil`.
/// - method: Method for request, default is `get`.
/// - encoding: Encoding for request, default is `default`
/// - headers: HTTP Header for request, default is nil.
public init(path: String,
parameters: Parameters? = nil,
method: Method = .get,
encoding: URLEncoding = .default,
headers: Headers? = nil) {

self.path = path
self.method = method
self.parameters = parameters
self.encoding = encoding
self.headers = headers
}
}
ResultConversion

ResultConversion define result convert.
Result define result parse way.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// Struct for result
open class Result {

/// Custom parse data, default is in "data"
open var data: (JSON) -> JSON
/// Custom error for failed call back, default is always no error.
open var error: (JSON) -> Error?

/// Struct for result
public init(data: @escaping (JSON) -> JSON = { $0["data"] },
error: @escaping (JSON) -> Error? = { _ in nil }) {
self.data = data
self.error = error
}
}

default parse data from "data".

default with no custom service error.

Initlization Lotus with host.

1
Center.default.configuration.host = { return "https://httpbin.org" }

Make a APIRouter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
enum APIRouter: RequestConversion {
case search(query: String, page: Int)

var request: Request {
switch self {
case let .search(query, page):
return Request(path: "/search", parameters: ["q": query, "offset": 10 * page])
}
}

@discardableResult
static func send(_ router: APIRouter) -> DataClient {
return Lotus.send(router)
}
}

Send request

1
APIRouter.send(.search(query: "Lotus", page: 1))

Advance Usage

Custom Parse Way

1
2
3
4
5
struct UserModel: ResultConversion {
static var result: Result {
return Result(data: { $0["newData"] }, error: { _ in nil })
}
}

⚠️ Only useful for generic and genericArray call back.

Custom Service Error

1
2
3
4
5
struct UserModel: ResultConversion {
static var result: Result {
return Result(data: { $0["data"] }, error: { $0["code"] == 401 ? nil : LotusError.customError(...) })
}
}

⚠️ If result error is not nil, it will call back with failed

⚠️ Only useful for generic and genericArray call back.

Custom Debug Center

All connection will through this DebugCenter. You can sub DebugCenter to custom netowork connection.

1
2
3
4
5
6
7
8
9
10
11
12
class CustomDebugCenter: DebugCenter {

override func send(_ client: DataClient, error: Error?) -> DataClient {
/// Custom send network.
return client
}

override func receive(_ task: URLSessionTask?, data: Data, error: Error?) -> (Data, Error?) {
/// Custom receive network.
return (data, error)
}
}
1
2
/// Set Debug center when initlization Lotus.
Center.default.configuration.debugCenter = CustomDebugCenter()

Custom Cache Center

1
2
3
4
5
6
7
8
9
10
11
12
13
class CustomCacheCenter: CacheCenter {
override func save(rawData data: Data?, withRawKey key: String) -> Data? {
/// Custom data when cache will been saved into file, nil if no data to cache.
return data
}
override func read(rawData data: Data?, withRawKey key: String) -> Data? {
/// Custom data when cache will been readed from file, nil if no cache.
return data
}
override func cache(byRawKey key: String) -> String {
return key.md5() ?? "Error MD5 with \(key)"
}
}
1
2
/// Set Cache center when initlization Lotus.
Center.default.configuration.cacheCenter = CustomCacheCenter()

Custom Log Center

1
2
3
4
5
6
7
8
9
10
11
class CustomLogCenter: LogCenter {
override func save(_ log: Log, witTime logTime: TimeInterval) -> String {
/// Custom log formatter to file.
return logTime.description + "," + log.raw.joined(separator: ",")
}

override func terminal(_ log: Log, witTime logTime: TimeInterval) -> String {
/// Custom log fromatter to terminal.
return "☁️" + Date(timeIntervalSince1970: logTime).description + log.raw.joined(separator: ",")
}
}
1
2
/// Set Log center when initlization Lotus.
Center.default.configuration.LogCenter = CustomLogCenter()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/// Default log for Client.
extension Client: CustomLogConvertible {

open var log: Log {
let log = Log()
log.raw["Task Identifier"] = delegate.task?.taskIdentifier.description ?? "Unknow Task Identifier"
log.raw["Begin Time"] = beginTime.description
log.raw["Method"] = request?.httpMethod ?? "Unknow Method"
log.raw["URL"] = request?.url?.absoluteString ?? "Unknow URL"
log.raw["HTTP Header"] = request?.allHTTPHeaderFields?.description ?? "Unknow HTTP Header"
log.raw["HTTP Body"] = request?.httpBody == nil ? "No HTTP Body" : String(data: request!.httpBody!, encoding: .utf8)
log.raw["Error"] = error?.localizedDescription ?? "No Error"
return log
}
}