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

253696343a018052ff394eb1f63e3d6f?s=128

Taketo Yoshida

September 16, 2016
Tweet

Transcript

  1.  .PEFSOBOE1SBDUJDBM /FUXPSLJOHJO4XJGU 5BLFUP:PTIJEB 姏莸⠁䏟UFDI

  2.  )FMMP 5BLFUP:PTIJEB!UBNBOZBO J04%FWFMPQFS!/JLLFJ 秵꬗ؽُ٦،٦،فٔךꟚ涪罏 4XJGUָ㥨ֹדׅ%

  3.  طحزٙ٦ؙفؚٗٓىؚٝכꅾ銲

  4.  "1*$MJFOUJTOPU&BTZ ؔؿٓ؎ٝ儗ך׋׭חر٦ة׾ٍؗحءُׅ׷ "1*ؙٔؒأزָ㣟侁׃׋㜥さٔزٓ؎׾遤ֲ 㹋⹡ח黝׃׋"1*ؙٓ؎،ٝز׾⡲䧭׃׋ְ

  5.  㹋⹡ِ٦أ؛٦أחさ׻ׇ׋ "1*$MJFOU׾⡲䧭ׅ׷חכ

  6.  IUUQTHJUIVCDPNUBNBOZBO,BCV[J;B5FDI ؟ٝفٕ؝٦س

  7.  1SPUPDPM0SJFOUFE /FUXPSL1SPHSBNNJOH

  8.  4XJGUד"1*$MJFOU׾⡲׷ 6TF"MBNPSF

  9.  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
  10. 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 
  11. 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
  12. 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
  13. 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
  14.  .PEFSO/FUXPSLJOHJO4XJGU "1*,JUIUUQTHJUIVCDPNJTILBXB"1*,JU 4XJTIIUUQTHJUIVCDPNUIPVHIUCPU4XJTI

  15. 

  16. 

  17. 

  18.  "1*$MJFOU׾ぐِ٦أ؛٦أ חさ׻ׇג䭁䓸ׅ׷ ս劤겗վ

  19. 6TF$BTF (FU6TFS3FRVFTU (FU"OOPVODFNFOU3FRVFTU "1* ٍؗحءُ堣腉׾䭯אؙٔؒأزה䭯׋זְؙٔؒأز "1* DBDIF 

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

    var cacheEnabled: Bool { get } /** * キャッシュが存在する場合、ResponseTypeを返す */ func cache() -> ResponseType? } ,BCV[J;B5FDITXJGU
  21. 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
  22. 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
  23. 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
  24. 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
  25. 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 
  26.  "1*$MJFOUXJUI$BDIF'FBUVSF (FU6TFS3FRVFTU (FU"OOPVODFNFOU3FRVFTU 3FRVFTU5ZQF 3FRVFTU5ZQF 3FRVFTU$BDIFBCMF JNQMFNFOUT

  27.  6TF$BTF (FU,FZXPSE-JTU3FRVFTU DBDIF "1* GBJM ♶㸜㹀ז㢩鿇"1*׾⢪欽׃גֶ׶ծٔزٓ؎׾⟣䠐ך㔐侧ד遤ְ׋ְ GBJM (FU,FZXPSE-JTU3FRVFTU "1*

    DBDIF 5IJSE"1* 5IJSE"1* 3FUSZ GBJM
  28.  protocol RequestRetryable { /** * リトライする回数 */ var retryCount:

    Int { get } /** * リトライするかどうかを判定する */ func canRetry(error: Error) -> Bool } extension RequestRetryable { var retryCount: Int { return 3 } } ,BCV[J;B5FDITXJGU
  29. 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
  30. 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 
  31. 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 ؙٔؒأز׾ٔزٓ؎ׅ׷
  32. class APIClient { ... private func performCachedRequest<T: RequestType & RequestCacheable>(

    _ request: T) -> T.ResponseType? { guard request.cacheEnabled else { return nil } return request.cache() } ...  ,BCV[J;B5FDITXJGU
  33. 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
  34. APIClient().sendRequest(GetKeywordListRequest()) { result in switch result { case .success(let keywordList):

    /* success handler */ break case .failure(let error): /* error handler */ break } } ,BCV[J;B5FDITXJGU 
  35.  "1*$MJFOUXJUI3FUSZ'FBUVSF (FU,FZXPSE-JTU3FRVFTU (FU"OOPVODFNFOU3FRVFTU 3FRVFTU5ZQF 3FRVFTU5ZQF 3FRVFTU$BDIFBCMF 3FRVFTU3FUSZBCMF (FU6TFS3FRVFTU 3FRVFTU5ZQF

    3FRVFTU$BDIFBCMF JNQMFNFOUT
  36.  "EWBOUBHFT ぐؙٔؒأز嫣ח䗳銲ז堣腉׾➰♷ׅ׷ֿהָדֹ׷ "1*$MJFOU׾⢪ֲ⩎6TFS$PEFַ׵䠐陎ׅ׷ֿהָ㼰זְ

  37.  %JTBEWBOUBHFT ؙٔؒأزח➰♷ׅ׷堣腉ָ㢳ֻז׷הؕؔأחז׷ O⦐ך堣腉׾ׅץג笨繎ׅ׷ךחOךػة٦ٝ׾剅ֻ䗳銲ָ֮׷

  38.  5IBOLZPVGPSZPVSMJTUFOJOH