Created
September 14, 2016 12:49
-
-
Save niwatako/7edf323cd5d001d7d99bc61042302316 to your computer and use it in GitHub Desktop.
実際書くと今まで聞いてきたものはだいたいこんな感じにかける。普段書くものでSwift3を書いていけば良いと思う #CodePiece #iphonekyoto
This file contains hidden or 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
struct ProfileResponse: APIResponse { | |
let name: String | |
let age: Int | |
init(json: JSONObject) throws { | |
name = try json.get("name") | |
age = try json.get("age") | |
} | |
} | |
protocol APIEndpoint { | |
var url: URL { get } | |
var method: HTTPMethod { get } | |
var queryParameters: APIParameters? { get } | |
var headers: APIHeaders? { get } | |
associatedtype Response: APIResponse | |
} | |
protocol JSONDecodable { | |
init(json: JSONObject) throws | |
} | |
protocol APIResponse: JSONDecodable { | |
} | |
struct JSONObject { | |
private let dictionary: [String: Any] | |
init(dictionary: [String: Any]) { | |
self.dictionary = dictionary | |
} | |
} | |
enum JSONDecodeError: Error { | |
case missingRequiredKey(String) | |
case unexpectedType(key: String, expected: Any.Type, actual: Any.Type) | |
} | |
func get<T: JSONPrimitive>(_ key: String) throws -> T { | |
guard let value = dictionary[key] else { | |
throw JSONDecodeError.missingRequiredKey(key) | |
} | |
guard let typed = value as? T else { | |
throw JSONDecodeError.unexpectedType( | |
key: key, | |
expected: T.self, | |
actual: type(of: value)) | |
} | |
return typed | |
} | |
enum HTTPMethod: String { | |
case GET | |
case HEAD | |
case POST | |
case PUT | |
case DELETE | |
} | |
typealias APIParameters = Dictionary<String, String?> | |
typealias APIHeaders = Dictionary<String, String> | |
fileprivate extension Dictionary { | |
var parameters: Dictionary { | |
return self | |
} | |
} | |
fileprivate extension APIEndpoint { | |
var request: URLRequest { | |
var urlComponents = URLComponents( | |
url: url, | |
resolvingAgainstBaseURL: true)! | |
urlComponents.queryItems = queryParameters? | |
.parameters.map(URLQueryItem.init) | |
var request = URLRequest(url: urlComponents.url!) | |
request.httpMethod = method.rawValue | |
request.allHTTPHeaderFields = headers | |
return request | |
} | |
} | |
struct APIClient { | |
let session: URLSession | |
} | |
enum APIResult<Response> { | |
case success(Response) | |
case failure(Error) | |
} | |
@discardableResult // 返り値不要なものは明示的に宣言する必要がある | |
func request<T: APIEndpoint>( | |
endpoint: T, | |
callback: @escaping ((APIResult<T.Response>) -> Void)) -> URLSessionTask { // 非同期実行するのでエスケーピングが必要 | |
let task = session.dataTask(with: endpoint.request) { (data, response, error) in | |
if let error = error { | |
callback(.failure(error)) | |
return | |
} | |
guard let data = data else { | |
callback(.failure(APIError.emptyBody)) | |
return | |
} | |
do { | |
guard let dictionary = try JSONSerialization.jsonObject(with: data) as? [String: Any] else { | |
throw APIError.unexpectedResponse | |
} | |
let response = try T.Response(json: JSONObject(dictionary: dictionary)) | |
callback(.success(response)) | |
} catch { | |
callback(.failure(error)) | |
} | |
} | |
task.resume() | |
return task | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment