Skip to content

Instantly share code, notes, and snippets.

@digoreis
Last active July 7, 2018 13:41
Show Gist options
  • Save digoreis/bc1cbf2bf678a943cc2601111191b3d3 to your computer and use it in GitHub Desktop.
Save digoreis/bc1cbf2bf678a943cc2601111191b3d3 to your computer and use it in GitHub Desktop.
Simple DSL in Swift to Networking
import Cocoa
public typealias RestKitSuccess = (HTTPURLResponse, Data?) -> Void
public typealias RestKitFailure = (HTTPURLResponse, RestKitError) -> Void
public protocol RestKitProcess {
func process(_ request: URLRequest) -> URLRequest
}
public enum RestKitError: Error {
case httpError(Int)
}
public class RestKit {
public let methodHttp : RestKitMethod
public var urlString : String = ""
public var params = [RestKitParam]()
public var headers = [RestKitHeader]()
public var body: RestKitBody = .none
public var successCallback: RestKitSuccess?
public var failureCallback: RestKitFailure?
public var process: [RestKitProcess] = [RestKitProcess]()
public var task: URLSessionDataTask?
private let session: URLSession
public static func create(method: RestKitMethod, session: URLSession = URLSession.shared) -> RestKit { return RestKit(method: method, session: session) }
init(method: RestKitMethod, session: URLSession) { self.methodHttp = method ; self.session = session}
public var completeURL: String { return self.urlString.appending(params.reduce("?", { return "\($0)&\($1.generate())" })) }
}
public enum RestKitHeader {
case custom(String,String)
case authorization(String)
case basicAuth(String,String)
public func generate() -> (key: String,value: String) {
switch self {
case .custom(let k,let v): return (key: k, value: v)
case .authorization(let v): return (key: "Authorization", value: v)
case .basicAuth(let user,let passcode):
let crypto = "\(user):\(passcode)"
let final = Data(crypto.utf8).base64EncodedString()
let v = "Basic \(final)"
return (key: "Authorization", value: v)
}
}
}
public enum RestKitBody {
case body(Data)
case none
public func toData() -> Data? {
switch self {
case .body(let value): return value
case .none: return nil
}
}
public func toString() -> String {
switch self {
case .body(let value): return String(data: value, encoding: .utf8) ?? ""
case .none: return ""
}
}
}
public enum RestKitMethod : CustomStringConvertible {
case get,post,put,delete,head
public var description: String {
switch self {
case .get: return "GET"
case .post: return "POST"
case .put: return "PUT"
case .delete: return "DELETE"
case .head: return "HEAD"
}
}
}
public enum RestKitParam {
case stringValue(String,String)
case intValue(String,Int)
}
public extension RestKitParam {
func generate() -> String {
switch self {
case .stringValue(let key, let value):
let keyString = key
let valueString = value.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? value
return "\(keyString)=\(valueString)"
case .intValue(let key, let value):
let keyString = key
let valueString = "\(value)"
return "\(keyString)=\(valueString)"
}
}
}
public extension RestKit {
public func url(_ url: String) -> RestKit {
self.urlString = url
return self
}
public func param(_ param: RestKitParam) -> RestKit {
self.params.append(param)
return self
}
public func body<T : Encodable>(_ body: T) -> RestKit {
let encoder = JSONEncoder()
if let data = try? encoder.encode(body) {
self.body = .body(data)
}
return self
}
public func header(_ header: RestKitHeader) -> RestKit {
self.headers.append(header)
return self
}
public func success(_ callback: @escaping RestKitSuccess) -> RestKit {
self.successCallback = callback
return self
}
public func failure(_ callback: @escaping RestKitFailure) -> RestKit {
self.failureCallback = callback
return self
}
}
public extension RestKit {
public func execute() -> Void {
guard let url = URL(string: completeURL) else { return }
var request = URLRequest(url: url)
request.httpMethod = String(describing: self.methodHttp)
request.httpBody = self.body.toData()
request = self.headers.reduce(request, { (result, header) -> URLRequest in
var newRequest = request
let h = header.generate()
newRequest.addValue(h.value, forHTTPHeaderField: h.key)
return newRequest
})
request = self.process.reduce(request, { (result, process) -> URLRequest in
return process.process(result)
})
self.task = self.session.dataTask(with: request) { (responseData, response, responseError) in
guard let response = response, let httpResponse = response as? HTTPURLResponse else { return }
self.task = nil
if let _ = responseError {
self.failureCallback?(httpResponse,.httpError(httpResponse.statusCode))
return
}
self.successCallback?(httpResponse,responseData)
}
self.task?.resume()
}
}
struct Proposal: Codable {
let title: String
}
let baseURL = "https://data.swift.org/swift-evolution/proposals"
var rest = RestKit.create(method: .get)
.url(baseURL)
.success({ response, proposals in
let str = String(data: proposals!, encoding: .utf8) ?? "Proposals empty"
print(str)
})
.failure { (response, error) in
print(response)
}
rest.execute()
@digoreis
Copy link
Author

digoreis commented Apr 18, 2018

//
// ViewController.swift
// TestForRestKit
//
// Created by Rodrigo Reis on 13/04/18.
// Copyright © 2018 Rodrigo Reis. All rights reserved.
//

import UIKit

extension Date {
func toMillis() -> Int64 {
return Int64(self.timeIntervalSince1970 * 1000)
}
}

class ViewController: UIViewController {

let baseURL = "https://gateway.marvel.com:443/v1/public/characters"
let publicApiKey = ""
let privateApiKey = ""
let timestamp = "\(Date().toMillis())"

var rest: RestKit?

override func viewDidLoad() {
    super.viewDidLoad()


   self.rest = RestKit.create(method: .get)
            .url(baseURL)
            .param(.stringValue("apikey", self.publicApiKey))
            .param(.stringValue("hash", self.hash()))
            .param(.stringValue("ts", self.timestamp))
            .success { (response, data) in
                guard let dataContent = data else { return }
                print(String(data: dataContent, encoding: .utf8) ?? "Error in Payload")
            }
            .failure { (response, error) in
                print(response)
            }
    
    self.rest?.execute()
}

func hash() -> String {
    let md5 = MD5(string: "\(self.timestamp)\(self.privateApiKey)\(self.publicApiKey)")
    return md5.map { String(format: "%02hhx", $0) }.joined()
}

private func MD5(string: String) -> Data {
    let messageData = string.data(using:.utf8)!
    var digestData = Data(count: Int(CC_MD5_DIGEST_LENGTH))

    _ = digestData.withUnsafeMutableBytes {digestBytes in
        messageData.withUnsafeBytes {messageBytes in
            CC_MD5(messageBytes, CC_LONG(messageData.count), digestBytes)
        }
    }

    return digestData
}

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment