問題
- AlamofireとSwiftTaskで汎用的にレスポンスをモデルにデコードするようなHTTP Clientをつくりたい
- 特定のAPIエンドポイントのレスポンスをデコードするロジックは、各モデルにもたせたい
SwiftJSONを受けて"何らかの"モデルにデコードするメソッドを持つProtocolを作る
import SwiftyJSON protocol JSONMappable{ associatedtype T static func mapJSON(json: SwiftyJSON.JSON) -> T }
associatedtype
をgenericsとして受け取れるprotocolなので、このprotocolをimplementするclassにおいてtypealias
でT
を指定すれば、その型を返すメソッドの実装でこのprotocolを満たせる。
このProtocolを満たすClassを作る
Model的なもの
import SwiftyJSON class User: NSObject, JSONMappable { let name: String let age: Int init(name: String, age: Int) { self.name = name self.age = age super.init() } // MARK: - JSONMappableの実装 typealias T = User // mapJSONの返り値としてUserを実装 static func mapJSON(json: JSON) -> T { return User( json["name"].stringValue, json["age"].intValue ) } }
genericなprotocolを返り値に取れるHTTPClientを作る
import Alamofire import SwiftTask import SwiftyJSON class HTTPClient: NSObject { static func request<T:JSONMappable>( method: Alamofire.Method, url: NSURL, params: [String: AnyObject]?, headers: [String:String] = ["Content-Type": "application/json"] ) -> Task<(Int64, Int64, Int64), T, NSError> { return Task<(Int64, Int64, Int64), T, NSError> { progress, fulfill, reject, configure in Alamofire.request(method, url, parameters: params, headers: headers, encoding: .JSON) .responseJSON { response -> Void in switch response.result { case .Success(let value): let json = JSON(rawValue: value) // TはJSONMappableなので、mapJSONメソッドを持っている if mapped = T.mapJSON(json!) as? T { fulfill(mapped) } else { reject(NSError(domain: "foo", code: 123, userInfo: nil)) } return case .Failure(let error): reject(error) return } } } } }
番外: APIエンドポイントとモデルを紐付けるクラスをつくる
本論では無いけど、HTTPClientを継承してエンドポイントとモデルの関連を固定するクラスをつくってみる
class UserAPI: HTTPClient { static func get(id: Int) -> Task<(Int64, Int64, Int64), User, NSError> { let url = NSURL(string: "http://localhost:8080/user/\(id)")! return request(.GET, url: url, params: [:]) } }
ゴール: ViewControllerから使う
UserAPI.get(1) .success { user -> Void in print("user取れた", user) } .failure { error, cancelled -> Void in print("失敗した", error) }
雑感
- 最近JSばっかりだったので、型とエディタが強力なのすごい気持ちいい
DRYな備忘録