Skip to content

Instantly share code, notes, and snippets.

@fellipecaetano
Last active October 12, 2017 23:02
Show Gist options
  • Save fellipecaetano/1c0450976528c852bdff00dbc67dd703 to your computer and use it in GitHub Desktop.
Save fellipecaetano/1c0450976528c852bdff00dbc67dd703 to your computer and use it in GitHub Desktop.
At attempt at solving networking. Just paste it inside an Xcode Playground and start from there.
import Foundation
struct HTTPRequest<T> {
let method: HTTPMethod
let path: String
let parameters: [String: Any]
let headers: [String: String]
let response: ExpectedHTTPResponse<T>
}
extension HTTPRequest: Authenticatable {
typealias Key = Authentication
func authenticated(with key: Authentication) -> HTTPRequest<T> {
return HTTPRequest(
method: .get,
path: path,
parameters: [
"token": key.token
].merging(parameters, uniquingKeysWith: { lhs, _ in lhs }),
headers: headers,
response: response
)
}
}
struct ExpectedHTTPResponse<T> {
let decoded: (Data) throws -> T
}
enum HTTPMethod {
case get
case post
}
/* Authenticated */
struct Authenticated<T: Authenticatable, K>: Authenticatable where T.Key == K {
typealias Key = K
private let key: K?
private let value: T
private init (key: K?, value: T) {
self.key = key
self.value = value
}
static func of(_ value: T) -> Authenticated<T, K> {
return Authenticated(key: nil, value: value)
}
func authenticated(with key: K) -> Authenticated<T, K> {
return Authenticated(key: key, value: value)
}
func map<V>(_ fn: (T) throws -> V) rethrows -> Authenticated<V, K> {
return Authenticated<V, K>(key: key, value: try fn(value))
}
func flatMap<V>(_ fn: (T) throws -> Authenticated<V, K>) rethrows -> Authenticated<V, K> {
return try map(fn).dematerialized()
}
func dematerialized() -> T {
return key.map(value.authenticated(with:)) ?? value
}
}
protocol Authenticatable {
associatedtype Key
func authenticated(with key: Key) -> Self
}
/* Endpoint */
struct Endpoint<B, T> {
private let baseURL: B
private let value: T
init (baseURL: B, value: T) {
self.baseURL = baseURL
self.value = value
}
func map<V>(_ fn: (T) throws -> V) rethrows -> Endpoint<B, V> {
return Endpoint<B, V>(baseURL: baseURL, value: try fn(value))
}
func flatMap<V>(_ fn: (T) throws -> Endpoint<B, V>) rethrows -> Endpoint<B, V> {
let args = try map(fn).dematerialized().value.dematerialized()
return Endpoint<B, V>(baseURL: args.baseURL, value: args.value)
}
func dematerialized() -> (baseURL: B, value: T) {
return (baseURL, value)
}
}
/* Request creators */
typealias LegacyAPIRequest<T> = Endpoint<LegacyAPIURL, HTTPRequest<T>>
typealias AuthenticatedLegacyAPIRequest<T> = Endpoint<LegacyAPIURL, Authenticated<HTTPRequest<T>, Authentication>>
typealias SearchAPIRequest<T> = Endpoint<SearchAPIURL, HTTPRequest<T>>
typealias AuthenticatedSearchAPIRequest<T> = Endpoint<SearchAPIURL, Authenticated<HTTPRequest<T>, Authentication>>
func create<T>(request: HTTPRequest<T>) -> LegacyAPIRequest<T> {
return .of(request)
}
func create<T>(request: HTTPRequest<T>) -> AuthenticatedLegacyAPIRequest<T> {
return .of(.of(request))
}
func create<T>(request: HTTPRequest<T>) -> SearchAPIRequest<T> {
return .of(request)
}
func create<T>(request: HTTPRequest<T>) -> AuthenticatedSearchAPIRequest<T> {
return .of(.of(request))
}
extension Endpoint where B == LegacyAPIURL {
static func of(_ value: T) -> Endpoint<B, T> {
return Endpoint(baseURL: LegacyAPIURL(), value: value)
}
}
extension Endpoint where B == SearchAPIURL {
static func of(_ value: T) -> Endpoint<B, T> {
return Endpoint(baseURL: SearchAPIURL(), value: value)
}
}
extension Authenticated where K == Authentication {
static func of(_ value: T) -> Authenticated<T, K> {
let key = Environment.current.authentication
return Authenticated(key: key, value: value)
}
}
/* Request definition */
func EventsByCategoryRequest(category: Category) -> AuthenticatedLegacyAPIRequest<[Event]> {
return create(
request: HTTPRequest(
method: .get,
path: "/events/category",
parameters: ["category": category],
headers: ["X-App-Token": "8asd8urjahdfue"],
response: APIEventsResponse()
)
)
}
func APIEventsResponse() -> ExpectedHTTPResponse<[Event]> {
return ExpectedHTTPResponse { _ in
throw APIEventsError()
}
}
/* Models */
struct Authentication {
let token = ""
}
struct LegacyAPIURL {
var url: URL {
return URL(string: "https://legacy.api.com")!
}
}
struct SearchAPIURL {
var url: URL {
return URL(string: "https://search.api.com")!
}
}
struct Category {}
struct Event {}
struct APIEventsError: Error {}
/* App */
struct Environment {
static let current = Environment()
let authentication = Authentication()
}
print(EventsByCategoryRequest(category: Category()))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment