Skip to content

Instantly share code, notes, and snippets.

@wleii
Last active October 31, 2017 09:19
Show Gist options
  • Save wleii/36f31f9c49b444a5e65d1d4a7124ee1a to your computer and use it in GitHub Desktop.
Save wleii/36f31f9c49b444a5e65d1d4a7124ee1a to your computer and use it in GitHub Desktop.
Swift: a top-level design for network request, base Alamofire
import Foundation
import Alamofire
enum HTTPResult<Value> {
case success(Value)
case failure(Error)
}
extension HTTPResult {
func map<T>(_ transform: ((Value) -> T)) -> HTTPResult<T> {
switch self {
case let .success(value):
return .success(transform(value))
case let .failure(error):
return .failure(error)
}
}
func flatMap<T>(_ transform: ((Value) throws -> T)) -> HTTPResult<T> {
switch self {
case let .success(value):
do {
return try .success(transform(value))
} catch {
return .failure(error)
}
case let .failure(error):
return .failure(error)
}
}
}
enum TransformError: Error, LocalizedError {
case notJsonType
var errorDescription: String? {
switch self {
case .notJsonType:
return "Not a JSON value Type"
}
}
}
struct Transform {
static func json(_ any: Any) throws -> [String: Any] {
if let dic = any as? [String: Any] {
return dic
}else {
throw TransformError.notJsonType
}
}
static func jsonArray(_ any: Any) throws -> [[String: Any]] {
if let dic = any as? [[String: Any]] {
return dic
}else {
throw TransformError.notJsonType
}
}
struct Model<T: Decodable> {
static func model(_ value: Data) throws -> T {
return try model(value)
}
static func model(_ value: Data) throws -> [T] {
return try model(value)
}
static func model(_ value: Data, using jsonDecoder: JSONDecoder = JSONDecoder()) throws -> T {
return try jsonDecoder.decode(T.self, from: value)
}
static func model(_ value: Data, using jsonDecoder: JSONDecoder = JSONDecoder()) throws -> [T] {
return try jsonDecoder.decode(T.self, from: value) as! [T]
}
static func model(_ value: [String: Any]) throws -> T {
return try model(value, using: JSONDecoder())
}
static func model(_ value: [[String: Any]]) throws -> [T] {
return try model(value, using: JSONDecoder())
}
static func model(_ value: [String: Any], using jsonDecoder: JSONDecoder = JSONDecoder()) throws -> T {
let data = try JSONSerialization.data(withJSONObject: value, options: JSONSerialization.WritingOptions.init(rawValue: 0))
return try model(data)
}
static func model(_ value: [[String: Any]], using jsonDecoder: JSONDecoder = JSONDecoder()) throws -> [T] {
let dataList = try value.map( {try JSONSerialization.data(withJSONObject: $0, options: JSONSerialization.WritingOptions.init(rawValue: 0)) })
return try dataList.map{ try jsonDecoder.decode(T.self, from: $0) }
}
}
}
public protocol NetworkRawResponse { }
extension DataResponse: NetworkRawResponse {}
extension DefaultDataResponse: NetworkRawResponse {}
extension DownloadResponse: NetworkRawResponse {}
extension DefaultDownloadResponse: NetworkRawResponse {}
open class Network {
public enum Method {
case get
case post
}
public enum Encoding {
case url
case json
case propertyList
case custom(ParameterEncoding)
}
private var url: URLConvertible?
private var method: Method = .get
private var parameters: Parameters = [:]
private var encoding: Encoding = .url
private var headers: HTTPHeaders = [:]
private var request: Request?
static func request(url: URL) -> Network {
let network = Network()
network.url = url
return network
}
private var rawResponse: ((NetworkRawResponse) -> Void)?
static func request(url: String) -> Network {
let network = Network()
network.url = url
return network
}
// TODO: Download
// TODO: Upload
func method(_ value: Method) -> Network {
method = value
return self
}
func parameters(_ value: [String: Any]) -> Network {
parameters = value
return self
}
func encoding(_ value: Encoding) -> Network {
encoding = value
return self
}
func headers(_ value: [String: String]) -> Network {
headers = value
return self
}
func rawResponse(_ response: @escaping (NetworkRawResponse) -> Void) -> Network {
rawResponse = response
return self
}
func response(_ completion: @escaping ((HTTPResult<Any>) -> Void)) -> Network {
// start request
guard let url = url else {
fatalError("please set request url")
}
let requestMethod: HTTPMethod
switch method {
case .get:
requestMethod = .get
case .post:
requestMethod = .post
}
let parameterEncoding: ParameterEncoding
switch encoding {
case .url:
parameterEncoding = URLEncoding.default
case .json:
parameterEncoding = JSONEncoding.default
case .propertyList:
parameterEncoding = PropertyListEncoding.default
case .custom(let encode):
parameterEncoding = encode
}
let dataRequest = Alamofire.request(url, method: requestMethod, parameters: parameters, encoding: parameterEncoding, headers: headers)
request = dataRequest
dataRequest
.responseJSON { (dataResponse) in
// this is call back rawReponse observer
self.rawResponse?(dataResponse)
switch dataResponse.result {
case let .success(value):
completion(.success(value))
case let .failure(error):
// deal error
completion(.failure(error))
}
}
return self
}
}
extension Network: CustomStringConvertible {
public var description: String {
return request?.description ?? "alamofire request haven't ready"
}
}
extension Network: CustomDebugStringConvertible {
public var debugDescription: String {
return request?.debugDescription ?? "alamofire request haven't ready"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment