-
-
Save augustorsouza/5954515e325bcddf83bd7aaddf94df25 to your computer and use it in GitHub Desktop.
Code Sample for Leveraging Structs and Generics in the Networking Layer with Swift 4 (An update to objc.io Swift Talk)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
import UIKit | |
import PlaygroundSupport | |
// MARK: HttpMethod | |
enum HttpMethod<Body> { | |
case get | |
case post(Body) | |
} | |
extension HttpMethod { | |
var methodString: String { | |
switch self { | |
case .get: return "GET" | |
case .post: return "POST" | |
} | |
} | |
func map<B>(f: (Body) -> B) -> HttpMethod<B> where B: Encodable { | |
switch self { | |
case .get: return .get | |
case .post(let body): return .post(f(body)) | |
} | |
} | |
} | |
// MARK: Resource | |
struct Resource<A> where A: Decodable { | |
let method: HttpMethod<Data> | |
let url: URL | |
let parse: (Data) -> A? = { data in return try? JSONDecoder().decode(A.self, from: data) } | |
} | |
extension Resource { | |
init<T>(method: HttpMethod<T>, url: URL) where T: Encodable { | |
self.method = method.map { json in try! JSONEncoder().encode(json) } | |
self.url = url | |
} | |
} | |
// MARK: ImageResource | |
struct ImageResource { | |
let url: URL | |
let method: HttpMethod<Data> | |
let parse: (Data) -> UIImage? | |
} | |
extension ImageResource { | |
init(imageUrl: URL) { | |
self.url = imageUrl | |
self.method = .get | |
self.parse = { data in return UIImage(data: data) } | |
} | |
} | |
// MARK: Networking | |
extension URLRequest { | |
init<A>(resource: Resource<A>) { | |
self.init(url: resource.url) | |
httpMethod = resource.method.methodString | |
if case let .post(data) = resource.method { httpBody = data } | |
} | |
init(imageResource: ImageResource) { | |
self.init(url: imageResource.url) | |
httpMethod = imageResource.method.methodString | |
} | |
} | |
final class Networking { | |
func load<A>(resource: Resource<A>, completion: @escaping (A?, URLRequest?, Error?) -> ()) { | |
let request = URLRequest(resource: resource) | |
URLSession.shared.dataTask(with: request) { data, response, error in | |
if let error = error { | |
completion(nil, request, error) | |
return | |
} | |
completion(data.flatMap(resource.parse), request, nil) | |
}.resume() | |
} | |
func loadImage(resource: ImageResource, completion: @escaping (UIImage?) -> ()) { | |
let request = URLRequest(imageResource: resource) | |
URLSession.shared.dataTask(with: request) { data, response, error in | |
if let error = error { | |
debugPrint(error) | |
completion(nil) | |
return | |
} | |
completion(data.flatMap(resource.parse)) | |
}.resume() | |
} | |
} | |
// Playground | |
// Get | |
struct IPAddress: Decodable { | |
let ip: String | |
} | |
let ipResource = Resource<IPAddress>(method: .get, url: URL(string: "https://api.ipify.org/?format=json")!) | |
Networking().load(resource: ipResource) { ipaddress, _, _ in | |
guard let ipaddress = ipaddress else { return } | |
print("\(ipaddress.ip) IP") | |
} | |
// Post | |
struct Company: Decodable { | |
let name: String | |
let email: String | |
} | |
let request = ["email": "[email protected]"] | |
let companyResource = Resource<Company>(method: .post(request), url: URL(string: "/company/email")!) | |
Networking().load(resource: companyResource) { company, _, _ in | |
guard let company = company else { return } | |
// print(company.name) | |
} | |
// Get Image | |
let imageResource = ImageResource(imageUrl: URL(string: "https://www.staples-3p.com/s7/is/image/Staples/s1000625_sc7")!) | |
Networking().loadImage(resource: imageResource) { image in | |
guard let image = image else { return } | |
image | |
} | |
PlaygroundPage.current.needsIndefiniteExecution = true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment