Skip to content

Instantly share code, notes, and snippets.

@ratkins
Last active June 18, 2017 10:20
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ratkins/246c985350d77cf209fdf50ecfe1b341 to your computer and use it in GitHub Desktop.
Save ratkins/246c985350d77cf209fdf50ecfe1b341 to your computer and use it in GitHub Desktop.
Simple, type-safe networking in Swift with zero dependencies
import Foundation
enum HTTPMethod {
case get
case post(data: Data)
}
protocol Request {
associatedtype ResponseType
var method: HTTPMethod { get }
var path: String { get }
var headers: [String: String]? { get }
func parse(_ data: Data) throws -> ResponseType
}
protocol Session {
func execute<R: Request>(_ request: R, completion: @escaping (Result<R.ResponseType, AnyError>) -> Void)
}
import Foundation
import Result
extension Request {
var isMethodGET: Bool {
switch method {
case .get: return true
default: return false
}
}
var isMethodPOST: Bool {
switch method {
case .post: return true
default: return false
}
}
var body: Data? {
switch method {
case .get: return nil
case .post(let body): return body
}
}
}
class MockSession: Session {
var value: Any?
var error: AnyError?
func execute<R: Request>(_ request: R, completion: @escaping (Result<R.ResponseType, AnyError>) -> Void) {
switch (value, error) {
case (.some(let value), _):
completion(Result(value: value as! R.ResponseType))
case (_, .some(let error)):
completion(Result(error: error))
default:
preconditionFailure("You must set one of either 'value' or 'error' properties on MockRequester!")
}
}
}
import Foundation
import Result
extension URLSession: Session {
var baseURL: URL {
return URL(string: "http://api.example.com")! // Or get from Info.plist
}
func execute<R: Request>(_ request: R, completion: @escaping (Result<R.ResponseType, AnyError>) -> Void) {
let url = URL(string: request.path, relativeTo: baseURL)!
var urlRequest = URLRequest(url: url)
urlRequest.allHTTPHeaderFields = request.headers
switch request.method {
case .get:
urlRequest.httpMethod = "GET"
case .post(let data):
urlRequest.httpMethod = "POST"
urlRequest.httpBody = data
}
let task = dataTask(with: urlRequest) { data, response, error in
let statusCode = (response as? HTTPURLResponse)?.statusCode
do {
switch (data, statusCode, error) {
case (let data?, let statusCode?, _) where 200..<300 ~= statusCode:
let parsed = try request.parse(data)
completion(.success(parsed))
case (_, let statusCode?, _):
throw NSError(domain: "RequestErrorDomain", code: statusCode, userInfo: [NSLocalizedDescriptionKey: "Received \(statusCode) status code from server"])
case (_, _, let error?):
throw error
default:
break
}
} catch (let error) {
completion(Result.failure(AnyError(error)))
}
}
task.resume()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment