How can you encapsulate network requests to make them easier to extend
Import Foundation struct User {let name: String let message: String} (data: Data) { guard let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] else { return nil } guard let name = obj["name"] as? String else { return nil } guard let message = obj["message"] as? String else {return nil} self.name = name self.message = message}} Protocol Request {var host: String {GET} var path: String {GET} var method: HTTPMethod {get} var parameter: [String: Any] {get} /// Set the return type of User to associatedType Response /// Data) -> Response?} // In order that any request can be sent in the same way, Extension request {func send(handler:@escaping (Response?) -> Void) {/// to initiate a network request, let url = url (string: host.appending(path))! var request = URLRequest(url: Url) request. HttpMethod = method. RawValue / / / this is following the closure of writing task = URLSession. Shared. DataTask (with: request) { data,res,error in if let data = data, let res = parse(data: data) { DispatchQueue.main.async { handler(res) } }else { DispatchQueue.main.async { handler(nil) } } } task.resume() } } struct UserRequest: Request {// struct UserRequest: Request {// String let host = "https://api.onevcat.com" var path: String { return "/user/\(name)" } let method: HTTPMethod = .GET let parameter: [String : Any] = [:] func parse(data: Data) -> User? { return User(data: Data)}} // Call request class HomeViewController: ViewController { override func viewDidLoad() { let request = UserRequest(name: "onecat") request.send { (user) in if let user = user { print(user.name,user.message) } } } }Copy the code
The main problem with the Request structure is that it integrates too many functions, not only defining the value of host, but also how to parse the data, and the network Request method send is bound together. This is not reasonable, it should only care about the entry of the Request and the expected response type
After upgrade
- The network request will be sent independently of the network request class
- The data is parsed and processed in the data model Response
Struct User: Decodable {struct User: Decodable {let name: String let message: String (data: Data) { guard let obj = try? JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] else { return nil } guard let name = obj["name"] as? String else { return nil } guard let message = obj["message"] as? String else {return nil} self.name = name self.message = message} static func parse(data: Data) -> User? {return User(Data: Data)}} String {case GET case POST} protocol Client {/** protocol Client {func send(_ r: Request, handler: @escaping (request.response?) -> Void)} The compiler will report an error because Request contains a protocol with associated types, so it cannot be treated as a separate type. */ func send<T: Request>(_ r: T, handle: @escaping(t.deliverse?) -> Void) var host: String {get}} struct URLSessionClient: Client {let host = "https://api.onevcat.com" func send<T>(_ r: T, handle: @escaping (T.Response?) -> Void) where T : Request { let url = URL(string: host.appending(r.path))! var request = URLRequest(url: url) request.httpMethod = r.method.rawValue let task = URLSession.shared.dataTask(with: request) { data , _ , error in if let data = data , let res = T.Response.parse(data: data) { DispatchQueue.main.async { handle(res) } }else{ DispatchQueue.main.async { handle(nil) } } } task.resume() } } Protocol Decodable {static func parse(data: parse) {static func parse(data: parse); Data) -> Self? Protocol Request {var path: String {get} var method: HTTPMethod {get} var parameter: [String: Any] {get} // set the return type of User to an association type for all requests. Associatedtype Response: Decodable} struct UserRequest: Request { typealias Response = User let name: String var path: String { return "/user/\(name)" } let method: HTTPMethod =.GET let parameter: [String: Any] = [:]} ViewController { override func viewDidLoad() { URLSessionClient().send(UserRequest(name: "onevcat")) { user in if let user = user { print("\(user.message) from \(user.name)") } } } }Copy the code