Alamofireの コードリーディングから Swift 3.0の知見を学ぶ

685d83e2ac4c228d9d87126f7d18e024?s=47 asakahara
September 28, 2016

Alamofireの コードリーディングから Swift 3.0の知見を学ぶ

Swift 3.0にいち早く対応したAlamofire 4.0のコードリーディングをすることで、得られた知見を紹介します。

685d83e2ac4c228d9d87126f7d18e024?s=128

asakahara

September 28, 2016
Tweet

Transcript

  1. "MBNPpSFͷ ίʔυϦʔσΟϯά͔Β 4XJGUͷ஌ݟΛֶͿ ୈೋճ5FDI5BML4XJGU גࣜձࣾιχοΫϜʔϒ ࡔݪ໌༟

  2. ࡔݪ໌༟ 5XJUUFS*%!BTBLBIBSB ϒϩάIUUQTBLBIBSBIBUFOBCMPHKQ w લ৬͸͸ͯͳ ژ౎ ͰJ04ΤϯδχΞ w ೥݄͔ΒιχοΫϜʔϒͷΤϯδχΞ w

    ޿ౡ ஍ݩ ژ౎ౡࠜ
  3. גࣜձࣾιχοΫϜʔϒ w ສਓҎ্͕༡ͿιʔγϟϧήʔϜͷاը։ ൃӡ༻ w )5.-ͰಈըΛ഑৴͢ΔHVJMF w 8FCαʔϏεʗεϚʔτϑΥϯΞϓϦ։ൃ w σβΠϯɾΠϥετ੍࡞

  4. ιχοΫϜʔϒౡࠜࣄۀॴ w ೥݄͔Β։ઃ w দߐӺ͔ΒंͰ෼ఔ౓ͷࢁͷ্ʹ͋ΔΦ ϑΟε w ݱࡏ͸ΤϯδχΞ໊͕̑ಇ͍ͯΔ

  5. None
  6. None
  7. None
  8. None
  9. ࠓ೔࿩͢͜ͱ w গ͚ͩ͠4XJGUɺͷ͓͞Β͍ w "MBNPpSFͷ௨৴ॲཧʹ͍ͭͯ w "MBNPpSF͔Βͷมߋ఺ͷιʔε͔ Β͍͔ͭ͘ͷॲཧΛϐοΫΞοϓ

  10. 4XJGU w ͱ͸΄΅ಉ༷Ͱओͳҧ͍͸NBD04ɺ J04ɺXBUDI04ɺUW044%,ͷαϙʔτ w Λ࢖͏ʹ͸#VJME4FUUJOHTͰ6TF-FHBDZ 4XJGUCVJMETFUUJOH:&4ʹ͢Δ w Ҡߦʹ͸/463-ͷΑ͏ʹ໌ࣔతʹΞϯϥοϓ͢Δ ඞཁ͕͋ΔมߋͳͲɺҰ෦ιʔείʔυͷมߋ͕ඞ

    ཁͳ৔߹΋
  11. 4XJGU w 4XJGU͔Βͷมߋ఺͕ଟ͘Ҡߦʹ͸ιʔε ίʔυͷमਖ਼͕ඞਢ w .JHSBUJPO"TTJTUBOU͸༻ҙ͞Ε͍ͯΔ͕׬શ ʹҠߦͰ͖Δͱ͸ݶΒͳ͍

  12. 4XJGUͰͷมߋ఺ w 1SPQPTBM4UBUVT w IUUQTBQQMFHJUIVCJPTXJGUFWPMVUJPO w 4XJGUͰ࣮૷͞Εͨ1SPQPTBM͸ݸ͋Δ

  13. "#*ͷ҆ఆԽͷݟૹΓ w "QQMJDBUJPO#JOBSZ*OUFSGBDF "#*  w ҟͳΔόʔδϣϯͷ4XJGUίϯύΠϥͰίϯύ Πϧͨ͠όΠφϦ͸ϦϯΫͰ͖ͳ͍ w ͭ·ΓίϯύΠϧͨ͠όʔδϣϯ͕ҟͳΔϥΠϒ

    ϥϦͳͲ͸ಈ࡞͠ͳ͍ͱ͍͏͜ͱ
  14. ϥΠϒϥϦΛ4XJGUͰ ެ։͍ͯ͠Δਓ͸ཁ஫ҙ

  15. 4XJGUͷมߋ఺ͷҰ෦ w ɺ͕ແ͘ͳͬͨ w $TUZMFͷGPSϧʔϓ͕ແ͘ͳͬͨ w OPFTDBQFσΟϨΫςΟϒ͕σϑΥϧτʹ w PQFOͱpMFQSJWBUFͷΞΫηεम০ࢠ͕௥Ճ w

    ϝιουݺͼग़͠ʹ࠷ॳͷҾ਺ͷϥϕϧ΋ඞਢ
  16. 4XJGUͷมߋ఺ͷҰ෦ʢଓ͖ w ؔ਺ͷύϥϝʔλ͔ΒWBS͕ແ͘ͳͬͨ w ΧϦʔԽؔ਺એݴߏจ͕ແ͘ͳͬͨ w 4XJGU͔Β҉໧ͷϒϦοδม׵͕ແ͘ͳͬͨ ·ͩ·ͩ͋Γ·͕͢͜ͷลͰʜ w IUUQTBQQMFHJUIVCJPTXJGUFWPMVUJPO

  17. ͳͥ"MBNPpSFΛબΜ͔ͩʁ w 4XJGUͷ࣌୅͔Β։ൃ͞Ε͍ͯΔ w HJUIVCͷελʔ͕ Λ௒͑Δఆ൪ͷ)551 ௨৴༻ϥΠϒϥϦ w 9DPEFCFUB൛͔Β4XJGUʹ΋͍ͪૣ͘ ରԠ͓ͯ͠Γɺ9DPEFͷϦϦʔεΑΓલʹ

    4XJGUʹରԠͨ͠"MBNPpSF͕ެ։͞ Εͨ
  18. ϝιουνΣʔϯͰҰͭͷϦΫΤετʹରͯ͠ෳ ਺ͷSFTQPOTFIBOEMFSΛ࢖͏ॲཧ͸ɺͲ͏΍ͬ ࣮ͯݱ͍ͯ͠Δ͔ʁ Alamofire.request("https://httpbin.org/get") .responseString { response in print("Response String:

    \(response.result.value)") } .responseJSON { response in print("Response JSON: \(response.result.value)") }
  19. ௨৴͢ΔࡍͷॲཧͷྲྀΕΛ "MBNPpSFSFRVFTUϝιο υ͔Β௥ͬͯΈΔ

  20.  "MBNPpSFSFRVFTUϝιουͷݺͼग़͠ <"MBNPpSFTXJGU>  4FTTJPO.BOBHFSSFRVFTUϝιουͷݺͼग़͠ <4FTTJPO.BOBHFSTXJGU> w 3FRVFTUΫϥεͷॳظԽ࣌ʹ5BTL%FMFHBUFΛੜ੒ͯ͠௨৴Λ։࢝͢Δ w 5BTL%FMFHBUFΛੜ੒͢ΔλΠϛϯάͰ0QFSBUJPO2VFVFΛ༻ҙ͢Δ

    w 0QFSBUJPO2VFVF͸͙͢ॲཧΛ։࢝͠ͳ͍Α͏ʹαεϖϯυ͓ͯ͘͠ let operationQueue = OperationQueue() operationQueue.maxConcurrentOperationCount = 1 operationQueue.isSuspended = true
  21.  %BUB3FRVFTUSFTQPOTFϝιουͰσʔλऔಘޙͷॲ ཧΛ5BTL%FMFHBHFͷPQFSBUJPO2VFVFʹ௥Ճ͢Δ <3FTQPOTF4FSJBMJ[BUJPOTXJGU> w SFTQPOTF+40/ɺSFTQPOTF4USJOHϝιουͳͲ͸಺෦Ͱ %BUB3FRVFTUSFTQPOTFϝιουΛݺͼग़͍ͯ͠Δ public func response<T:

    DataResponseSerializerProtocol>( queue: DispatchQueue? = nil, responseSerializer: T, completionHandler: @escaping (DataResponse<T.SerializedObject>) -> Void) -> Self { delegate.queue.addOperation { let result = responseSerializer.serializeResponse( self.request, self.response, self.delegate.data, self.delegate.error )
  22.  ௨৴׬ྃ࣌ʹαεϖϯυͨ͠2VFVFͷॲཧΛ࠶։͢ Δ 63-4FTTJPO5BTL%FMFHBUFΛ࣮૷ <5BTL%FMFHBUFTXJGU> w RVFVFͷॲཧ͕࠶։͢ΔͷͰͷॲཧ͕࣮ߦ͞Ε࣮ࡍͷσʔλΛ औಘͰ͖Δ func urlSession(_

    session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) { if let taskDidCompleteWithError = taskDidCompleteWithError { taskDidCompleteWithError(session, task, error) } else { if let error = error { if self.error == nil { self.error = error } if let downloadDelegate = self as? DownloadTaskDelegate, let resumeData = (error as NSError)userInfo[NSURLSessionDownloadTaskResumeData] as? Data { downloadDelegate.resumeData = resumeData } } queue.isSuspended = false } }
  23. ͭ·Γ2VFVFʹDMPTVSFͷॲཧΛ೚ͤΔ͜ͱͰɺෳ਺ͷ SFTQPOTFIBOEMFS͕ॱ൪ʹॲཧΛߦ͏͜ͱΛɺγϯϓ ϧʹ࣮ݱ͍ͯ͠Δ Alamofire.request("https://httpbin.org/get") .responseString { response in print("Response String:

    \(response.result.value)") } .responseJSON { response in print("Response JSON: \(response.result.value)") }
  24. 3FRVFTU3FUSJFS ϦΫΤετͷࣗಈϦτϥΠػೳʹ͍ͭͯ΋ݟͯΈΔ class OAuth2Handler: RequestAdapter, RequestRetrier { public func should(_

    manager: SessionManager, retry request: Request, with error: Error, completion: RequestRetryCompletion) { if let response = request.task.response as? HTTPURLResponse, response.statusCode == 401 { completion(true, 1.0) // retry after 1 second } else { completion(false, 0.0) // don't retry } } } let sessionManager = SessionManager() sessionManager.retrier = OAuth2Handler()
  25. 3FRVFTU3FUSJFSͷߏ੒ 3FRVFTU3FUSJFSͷTIPVMEϝιουΛ࣮૷͢Δ͚ͩ public typealias RequestRetryCompletion = (_ shouldRetry: Bool, _

    timeDelay: TimeInterval) -> Void public protocol RequestRetrier { func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: @escaping RequestRetryCompletion) }
  26. if let retrier = retrier, let error = error {

    retrier.should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, delay in guard shouldRetry else { completeTask(session, task, error) ; return } DispatchQueue.utility.after(delay) { [weak self] in guard let strongSelf = self else { return } let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false if retrySucceeded, let task = request.task { strongSelf[task] = request return } else { completeTask(session, task, error) } } } ࢓૊Έ͸௨৴׬ྃ࣌ʹ3FRVFTU3FUSJFS͕ 4FTTJPO%FMFHBUFʹઃఆ͞Ε͍ͯΕ͹TIPVMEϝιου Λ࣮ߦͯ͠Ҿ਺ʹࢦఆ͞ΕͨEFMBZޙʹ௨৴Λ࠶࣮ߦ͢ Δͱ͍͏ࢸͬͯγϯϓϧͳ࣮૷<4FTTJPO%FMFHBUFTXJGU>
  27. "MBNPpSF͕ 4XJGUʹରԠ͍ͯ͠Δ ՕॴͰؾʹͳͬͨ ෦෼ͷҰ෦ΛϐοΫΞοϓ

  28. &SSPSϓϩτίϧ 1SPQPTBM4& extension AFError: LocalizedError { public var errorDescription: String?

    { switch self { case .parameterEncodingFailed(let reason): return reason.localizedDescription w 0CKFDUJWF$ͷ/4&SSPS͕&SSPSʹϒϦοδ͞ΕΔΑ͏ʹͳͬͨͷʹ൐͍ɺ "'&SSPSͱ͍͏৽͍͠ΤϥʔγεςϜ͕࣮૷͞Εͨ w -PDBMJ[FE&SSPSϓϩτίϧ͕࣮૷͞Ε͍ͯΔͷͰɺϩʔΧϥΠζͨ͠આ໌͕௥ՃͰ͖Δ w &SSPS5ZQF͕&SSPSϓϩτίϧʹมߋ͞Εͨ public enum AFError: Error { public enum ParameterEncodingFailureReason { case missingURL
  29. !EJTDBSEBCMF3FTVMU 1SPQPTBM4& @discardableResult public func request(_ urlRequest: URLRequestConvertible) -> DataRequest

    { return SessionManager.default.request(urlRequest) } w ฦΓ஋ͷ͋Δؔ਺ͷฦΓ஋Λར༻͍ͯ͠ͳ͍ͱσϑΥ ϧτͰܯࠂ͢Α͏ʹͳͬͨͨΊɺฦΓ஋Λར༻͢Δ ඞཁ͕ͳ͍ؔ਺ʹ͸!EJTDBSEBCMF3FTVMUΞϊ ςʔγϣϯΛ͚ͭΔ͜ͱͰܯࠂ͠ͳ͍Α͏ʹ͢Δ
  30. !FTDBQJOH 1SPQPTBM4& public func responseJSON( queue: DispatchQueue? = nil, options:

    JSONSerialization.ReadingOptions = .allowFragments, completionHandler: @escaping (DataResponse<Any>) -> Void) -> Self { return response( queue: queue, responseSerializer: DataRequest.jsonResponseSerializer(options: options), completionHandler: completionHandler ) } w !OPFTDBQFͷڍಈ͕σϑΥϧτʹͳͬͨͨΊɺ Ϋϩʔδϟ͕είʔϓ͔Βൈ͚ͯ΋ଘࡏ͠ଓ͚ Δͱ͖ʹ͸!FTDBQJOHΛBUUSJCVUFͱͯ͠෇ ༩͢Δ
  31. PQFO 1SPQPTBM4& open class SessionDelegate: NSObject { // MARK: URLSessionDelegate

    Overrides open var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)? open var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))? w PQFOɺQVCMJDͱ΋ʹଞͷϞδϡʔϧ͔Β࢖༻ Ͱ͖Δ͕ɺQVCMJD͸ܧঝ͕Ͱ͖ͣPQFOͰͷΈ ܧঝ͕Մೳ
  32. pMFQSJWBUF 1SPQPTBM4& extension NSNumber { fileprivate var isBool: Bool {

    return CFBooleanGetTypeID() == CFGetTypeID(self) } } w 4XJGUY·Ͱ͸ಉ͡ϑΝΠϧ಺Ͱ͋Ε͹QSJWBUF Ͱ΋ΞΫηεͰ͖͕ͨɺ4XJGU͔Β͸໌ࣔత ʹpMFQSJWBUFͱ͠ͳ͚Ε͹ΞΫηεग़དྷͳ͍
  33. FOVNͷDBTFΛMPXFSDBTFʹ 1SPQPTBM4&ɺ4&ɺ4& public enum NetworkReachabilityStatus { case unknown case notReachable

    case reachable(ConnectionType) } w UZQFͱQSPUPDPMҎ֎͸MPXFSDBTFΛ࢖͑ͱ"1*σβΠϯΨΠυ ϥΠϯʹ͋ΔͷͰFOVNͷDBTF΋MPXFSDBTFʹมߋ͞Ε͍ͯΔ w "1*σβΠϯΨΠυϥΠϯͷৄࡉ͸ԼهΛࢀর w IUUQTTXJGUPSHEPDVNFOUBUJPOBQJEFTJHOHVJEFMJOFT
  34. "OZ0CKFDU"OZ 1SPQPTBM4& if let temporaryURL = response?.temporaryURL, let data =

    try? Data(contentsOf: temporaryURL), let jsonObject = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions(rawValue: 0)), let json = jsonObject as? [String: Any], let args = json["args"] as? [String: String] { w 0CKFDUJWF$ͷJE͕"OZΛฦ͢Α͏ʹͳͬͨͷͰɺ "OZ0CKFDU͔Β"OZΛ࢖͏Α͏ʹมߋ͍ͯ͠Δ w ྫ͑͹+40/4FSJBMJ[BUJPOKTPO0CKFDU͕ฦ͢ܕ΋ "OZ0CKFDU͔Β"OZʹͳ͍ͬͯΔ
  35. ($%ͷॻ͖ํ͕4XJGUϥΠΫʹ 1SPQPTBM4& // Swift 2.2ͷ৔߹ dispatch_async(queue ?? dispatch_get_main_queue()) { completionHandler(response)

    } // Swift 3.0ͷ৔߹ (queue ?? DispatchQueue.main).async { completionHandler(downloadResponse) } w EJTQBUDI@RVFVF@Uͷ୅ΘΓʹ%JTQBUDI2VFVFܕ͕ ࢖͑ΔͳͲ($%͕4XJGUΆ͘ॻ͚ΔΑ͏ʹͳͬͨ
  36. ͦͷଞͷมߋ఺Ͱؾʹͳͬͨ ࡉ͔͍ॻ͖ํͳͲ

  37. EFGFSͰͷޙ࢝຤ private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {

    let inputStream = bodyPart.bodyStream inputStream.open() defer { inputStream.close() } w EFGFSΛ࢖͏͜ͱͰείʔϓΛൈ͚Δࡍʹඞͣ *OQVU4USFBNΛDMPTF͢ΔΑ͏ʹมߋ͍ͯ͠Δ w PQFOͷ͙͢Լʹॻ͘͜ͱͰDMPTFॲཧΛ͍ͯ͠Δ͜ͱ͕ Ұ໨Ͱ෼͔Δ
  38. JGMFUͰෳ਺એݴ͢Δ࣌ͷॻ͖ํ if let originalRequest = task.originalRequest, let headers = originalRequest.allHTTPHeaderFields,

    let authorizationHeaderValue = headers["Authorization"] { w MFUΛলུ͠ͳ͘ͳͬͨͷͰɺJGͷޙΛ͙͢վߦ͠ ͯMFUΛলུͤͣҰߦͣͭॻ͍͍ͯͯ෼Γ΍͍͢
  39. ·ͱΊ w "MBNPpSF͸͔ͳΓૣ͍ஈ֊Ͱ4XJGUʹରԠ͠ ͍ͯΔͷͰࢀߟʹ͢Δͷʹͪΐ͏ͲΑ͔ͬͨ w 4XJGUͷίʔσΟϯάن໿ΛܾΊΔࡍʹ΋ࢀߟʹͳΔ ͜ͱ͕ଟ͍ w 4XJGUͰͷྑ͍ॻ͖ํ΍ɺมߋ఺Λ೺Ѳ͢Δͷʹ ͸࣮ࡍʹιʔείʔυΛݟΔํ͕ཧղ͠΍͍͢৔߹

    ͕ଟ͍ͷͰɺ༷ʑͳ4XJGUͰॻ͔ΕͨίʔυΛݟΔ͜ ͱΛΦεεϝ͠·͢
  40. ࠷ޙʹ

  41. ౡࠜʹڵຯ͕͋Δ ΤϯδχΞͷํΛืूͯ͠·͢

  42. ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ