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

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

asakahara
September 28, 2016

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

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

asakahara

September 28, 2016
Tweet

More Decks by asakahara

Other Decks in Technology

Transcript

  1.  "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
  2.  %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 )
  3.  ௨৴׬ྃ࣌ʹαεϖϯυͨ͠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 } }
  4. 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()
  5. 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) }
  6. 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>
  7. &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
  8. !EJTDBSEBCMF3FTVMU 1SPQPTBM4& @discardableResult public func request(_ urlRequest: URLRequestConvertible) -> DataRequest

    { return SessionManager.default.request(urlRequest) } w ฦΓ஋ͷ͋Δؔ਺ͷฦΓ஋Λར༻͍ͯ͠ͳ͍ͱσϑΥ ϧτͰܯࠂ͢Α͏ʹͳͬͨͨΊɺฦΓ஋Λར༻͢Δ ඞཁ͕ͳ͍ؔ਺ʹ͸!EJTDBSEBCMF3FTVMUΞϊ ςʔγϣϯΛ͚ͭΔ͜ͱͰܯࠂ͠ͳ͍Α͏ʹ͢Δ
  9. !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ͱͯ͠෇ ༩͢Δ
  10. 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ͰͷΈ ܧঝ͕Մೳ
  11. pMFQSJWBUF 1SPQPTBM4& extension NSNumber { fileprivate var isBool: Bool {

    return CFBooleanGetTypeID() == CFGetTypeID(self) } } w 4XJGUY·Ͱ͸ಉ͡ϑΝΠϧ಺Ͱ͋Ε͹QSJWBUF Ͱ΋ΞΫηεͰ͖͕ͨɺ4XJGU͔Β͸໌ࣔత ʹpMFQSJWBUFͱ͠ͳ͚Ε͹ΞΫηεग़དྷͳ͍
  12. 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
  13. "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ʹͳ͍ͬͯΔ
  14. ($%ͷॻ͖ํ͕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Ά͘ॻ͚ΔΑ͏ʹͳͬͨ
  15. 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ॲཧΛ͍ͯ͠Δ͜ͱ͕ Ұ໨Ͱ෼͔Δ
  16. JGMFUͰෳ਺એݴ͢Δ࣌ͷॻ͖ํ if let originalRequest = task.originalRequest, let headers = originalRequest.allHTTPHeaderFields,

    let authorizationHeaderValue = headers["Authorization"] { w MFUΛলུ͠ͳ͘ͳͬͨͷͰɺJGͷޙΛ͙͢վߦ͠ ͯMFUΛলུͤͣҰߦͣͭॻ͍͍ͯͯ෼Γ΍͍͢