Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Modern and Practical Networking in Swift

Modern and Practical Networking in Swift

歌舞伎座.tech#11「Swiftプログラミング勉強会」
http://kbkz.connpass.com/event/37480/presentation/
で話したスライド 

サンプルコード: https://github.com/tamanyan/KabuziZaTech

Taketo Yoshida

September 16, 2016
Tweet

More Decks by Taketo Yoshida

Other Decks in Programming

Transcript

  1.  protocol RequestType { associatedtype ResponseType var baseURL: String {

    get } var method: HTTPMethod { get } var path: String { get } var parameters: [String: AnyObject] { get } var headers: [String: String] { get } /* HTTPのResponse Bodyからオブジェクトを返す */ func responseFromObject(_ object: Data) -> ResponseType? } extension RequestType { var baseURL: String { return "https://..." } var headers: [String: String] { return [:] } var parameters: [String: AnyObject] { return [:] } var URL: String { return self.baseURL + self.path } } ,BCV[J;B5FDITXJGU
  2. class APIClient { func sendRequest<T: RequestType>(_ request: T, completion: @escaping

    (Result<T.ResponseType>) -> Void) { Alamofire.request(request.URL, method: request.method, parameters: request.parameters, headers: request.headers).responseData { response in switch response.result { case .success(let value): guard let responseData = request.responseFromObject(value) else { return completion(Result.failure(/* fail to parse error */)) } return completion(Result.success(responseData)) case .failure(let error): return completion(Result.failure(error)) } } } } ,BCV[J;B5FDITXJGU 
  3. class APIClient { func sendRequest<T: RequestType>(_ request: T, completion: @escaping

    (Result<T.ResponseType>) -> Void) { Alamofire.request(request.URL, method: request.method, parameters: request.parameters, headers: request.headers).responseData { response in switch response.result { case .success(let value): guard let responseData = request.responseFromObject(value) else { return completion(Result.failure(/* fail to parse error */)) } return completion(Result.success(responseData)) case .failure(let error): return completion(Result.failure(error)) } } } } ,BCV[J;B5FDITXJGU  䒷侧ך$MPTVSFָꟼ侧ךأ؝٦ف׾馄ִג׮ 㶷㖈ׅ׷㜥さ!FTDBQJOH׾אֽ׷ 4XJGU
  4. struct GetUserRequest: RequestType { typealias ResponseType = User let id:

    String var path: String { return "/v1/user/\(self.id)" } var method: HTTPMethod { return .get } func responseFromObject(_ object: Data) -> User? { return User(object) } }  ,BCV[J;B5FDITXJGU
  5. APIClient().sendRequest(GetUserRequest(id: "id")) { result in switch result { case .success(let

    user): /* success handler */ break case .failure(let error): /* error handler */ break } }  ,BCV[J;B5FDITXJGU
  6. 

  7. 

  8. 

  9.  protocol RequestCacheable { associatedtype ResponseType /** * キャッシュ有効フラグ */

    var cacheEnabled: Bool { get } /** * キャッシュが存在する場合、ResponseTypeを返す */ func cache() -> ResponseType? } ,BCV[J;B5FDITXJGU
  10. struct GetUserRequest: RequestType, RequestCacheable { typealias ResponseType = User let

    id: String var cacheEnabled = true var path: String { return "/v1/user/\(self.id)" } var method: HTTPMethod { return .get } func responseFromObject(_ object: Data) -> User? { return User(object) } func cache() -> User? { /* Realmなどを利用する */ return CacheManger.shared.fetch(self.id) as? User } }  ,BCV[J;B5FDITXJGU
  11. class APIClient { private func performRequest<T: RequestType>(_ request: T, completion:

    @escaping (Result<T.ResponseType>) -> Void) { /* Alamofireでリクエストを送る */ } func sendRequest<T: RequestType>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { self.performRequest(request, completion: completion) } func sendRequest<T: RequestType & RequestCacheable>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { if request.cacheEnabled { if let data = request.cache() { return completion(Result.success(data)) } } self.performRequest(request, completion: completion) } }  ,BCV[J;B5FDITXJGU
  12. class APIClient { private func performRequest<T: RequestType>(_ request: T, completion:

    @escaping (Result<T.ResponseType>) -> Void) { /* Alamofireでリクエストを送る */ } func sendRequest<T: RequestType>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { self.performRequest(request, completion: completion) } func sendRequest<T: RequestType & RequestCacheable>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { if request.cacheEnabled { if let data = request.cache() { return completion(Result.success(data)) } } self.performRequest(request, completion: completion) } }  ,BCV[J;B5FDITXJGU 1SPUPDPM$PNQPTJUJPOח״׷5ZQF$POTUSBJOT 4XJGU
  13. class APIClient { private func performRequest<T: RequestType>(_ request: T, completion:

    @escaping (Result<T.ResponseType>) -> Void) { /* Alamofireでリクエストを送る */ } func sendRequest<T: RequestType>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { self.performRequest(request, completion: completion) } func sendRequest<T: RequestType & RequestCacheable>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { if request.cacheEnabled { if let data = request.cache() { return completion(Result.success(data)) } } self.performRequest(request, completion: completion) } }  ,BCV[J;B5FDITXJGU ٍؗحءָُ㶷㖈ׅ׸לծ⽯䏟ח3FUVSO
  14. APIClient().sendRequest(GetUserRequest(id: "id", cacheEnabled: true)) { result in switch result {

    case .success(let user): /* success handler */ break case .failure(let error): /* error handler */ break } } ,BCV[J;B5FDITXJGU 
  15.  protocol RequestRetryable { /** * リトライする回数 */ var retryCount:

    Int { get } /** * リトライするかどうかを判定する */ func canRetry(error: Error) -> Bool } extension RequestRetryable { var retryCount: Int { return 3 } } ,BCV[J;B5FDITXJGU
  16. struct GetKeywordListRequest: RequestType, RequestCacheable, RequestRetryable { typealias ResponseType = KeywordList

    var cacheEnabled: Bool = false var retryCount: Int { return 4 } var path: String { return "/v1/keywords" } var method: HTTPMethod { return .get } func responseFromObject(_ object: Data) -> KeywordList? { return KeywordList(object) } func canRetry(error: Error) -> Bool { return /* conditions of retry */ ? true : false } func cache() -> KeywordList? { return CacheManger.shared.fetch("keywords") as? KeywordList } }  ,BCV[J;B5FDITXJGU
  17. class APIClient { private func performRetryableRequest<T: RequestType & RequestRetryable>( _

    request: T, retryCounter: Int = 0, completion: @escaping (Result<T.ResponseType>) -> Void) { Alamofire.request(request.URL, method: request.method, parameters: request.parameters, headers: request.headers).responseData { response in switch response.result { case .success(let value): guard let responseData = request.responseFromObject(value) else { return completion(Result.failure(/* fail to parse error */)) } return completion(Result.success(responseData)) case .failure(let error): if request.retryCount > retryCounter && request.canRetry(error: error) { return self.performRetryableRequest(request, retryCounter: retryCounter + 1, completion: completion) } return completion(Result.failure(error)) } } } ... ,BCV[J;B5FDITXJGU 
  18. class APIClient { private func performRetryableRequest<T: RequestType & RequestRetryable>( _

    request: T, retryCounter: Int = 0, completion: @escaping (Result<T.ResponseType>) -> Void) { Alamofire.request(request.URL, method: request.method, parameters: request.parameters, headers: request.headers).responseData { response in switch response.result { case .success(let value): guard let responseData = request.responseFromObject(value) else { return completion(Result.failure(/* fail to parse error */)) } return completion(Result.success(responseData)) case .failure(let error): if request.retryCount > retryCounter && request.canRetry(error: error) { return self.performRetryableRequest(request, retryCounter: retryCounter + 1, completion: completion) } return completion(Result.failure(error)) } } } ...  ,BCV[J;B5FDITXJGU ؙٔؒأز׾ٔزٓ؎ׅ׷
  19. class APIClient { ... private func performCachedRequest<T: RequestType & RequestCacheable>(

    _ request: T) -> T.ResponseType? { guard request.cacheEnabled else { return nil } return request.cache() } ...  ,BCV[J;B5FDITXJGU
  20. class APIClient { ... func sendRequest<T: RequestType>(_ request: T, completion:

    @escaping (Result<T.ResponseType>) -> Void) { /* performRequest */ } func sendRequest<T: RequestType & RequestCacheable>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { /* performCachedRequest & performRequest */ } func sendRequest<T: RequestType & RequestRetryable>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { /* performRetryableRequest */ } func sendRequest<T: RequestType & RequestCacheable & RequestRetryable>(_ request: T, completion: @escaping (Result<T.ResponseType>) -> Void) { /* performCachedRequest & performRetryableRequest */ } }  ,BCV[J;B5FDITXJGU
  21. APIClient().sendRequest(GetKeywordListRequest()) { result in switch result { case .success(let keywordList):

    /* success handler */ break case .failure(let error): /* error handler */ break } } ,BCV[J;B5FDITXJGU