Skip to content

Instantly share code, notes, and snippets.

@fmo91
Last active May 30, 2019 05:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fmo91/7a88b4bf546a7037bac84401f0799131 to your computer and use it in GitHub Desktop.
Save fmo91/7a88b4bf546a7037bac84401f0799131 to your computer and use it in GitHub Desktop.
Minimal yet modular and testable networking layer written in Swift 4 over URLSession using Codable, protocol extensions and associatedtypes.
//
// Konex.swift
// Konex
//
// Created by Fernando Ortiz on 24/5/18.
// Copyright © 2018 Fernando Ortiz. All rights reserved.
//
import Foundation
public enum KonexError: Swift.Error {
case invalidURL
case noData
}
public enum HTTPMethod: String {
case get = "GET"
case post = "POST"
case put = "PUT"
case delete = "DELETE"
case patch = "PATCH"
}
public struct RequestData {
public let path: String
public let method: HTTPMethod
public let params: [String: Any?]?
public let headers: [String: String]?
public init (
path: String,
method: HTTPMethod = .get,
params: [String: Any?]? = nil,
headers: [String: String]? = nil
) {
self.path = path
self.method = method
self.params = params
self.headers = headers
}
}
public protocol RequestType {
associatedtype ResponseType: Codable
var data: RequestData { get }
}
public extension RequestType {
public func execute (
dispatcher: NetworkDispatcher = URLSessionNetworkDispatcher.instance,
onSuccess: @escaping (ResponseType) -> Void,
onError: @escaping (Error) -> Void
) {
dispatcher.dispatch(
request: self.data,
onSuccess: { (responseData: Data) in
do {
let jsonDecoder = JSONDecoder()
let result = try jsonDecoder.decode(ResponseType.self, from: responseData)
DispatchQueue.main.async {
onSuccess(result)
}
} catch let error {
DispatchQueue.main.async {
onError(error)
}
}
},
onError: { (error: Error) in
DispatchQueue.main.async {
onError(error)
}
}
)
}
}
public protocol NetworkDispatcher {
func dispatch(request: RequestData, onSuccess: @escaping (Data) -> Void, onError: @escaping (Error) -> Void)
}
public struct URLSessionNetworkDispatcher: NetworkDispatcher {
public static let instance = URLSessionNetworkDispatcher()
private init() {}
public func dispatch(request: RequestData, onSuccess: @escaping (Data) -> Void, onError: @escaping (Error) -> Void) {
guard let url = URL(string: request.path) else {
onError(KonexError.invalidURL)
return
}
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = request.method.rawValue
do {
if let params = request.params {
urlRequest.httpBody = try JSONSerialization.data(withJSONObject: params, options: [])
}
} catch let error {
onError(error)
return
}
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
onError(error)
return
}
guard let _data = data else {
onError(KonexError.noData)
return
}
onSuccess(_data)
}.resume()
}
}
//
// KonexExample.swift
// Konex
//
// Created by Fernando Ortiz on 24/5/18.
// Copyright © 2018 Fernando Ortiz. All rights reserved.
//
import UIKit
struct User: Codable {
let id: Int
let name: String
let username: String
}
struct GetAllUsers: RequestType {
typealias ResponseType = [User]
var data: RequestData {
return RequestData(path: "http://jsonplaceholder.typicode.com/users")
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
GetAllUsers().execute(
onSuccess: { (users: [User]) in
},
onError: { (error: Error) in
}
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment