2016년 10월 26일 수요일

Alamofire 소스 분석 2


  • Alamofire --- SessionManager --- SessionDelegate --- Request --- TaskDelegate -- Response


Alamofire

  • 사용자가 웹주소를 String, URL, URLComponents으로 사용하면 이를 URL로 변경해주는 URLConvertible을 제공한다.
  • 사용자가 URLRequest를 뽑아낼 수 있는 URLRequestConvertible을 사용할 수도 있다.

SessionManager

  • default SessionManager를 제공한다.
    • URLSessionConfiguration을 사용해서 항상 사용할 HTTP 헤더를 지정한다: Accept-Encoding, Accept-Language, User-Agent
  • request, download, upload, stream
  • SessionDelegate를 URLSession에 연결하여 연결 세션에 대한 콜백을 받도록 해준다.
    • 보통 콜백이 불리면 상황에 따라 Request의 delegate를 호출한다.
  • "delegate[task] = request"를 통해 delegate가 request를 저장하고 있도록 한다.
  • 기본은 request.resume()를 호출하여 request에 관련된 task를 실행한다.
  • 서버 요청은 별도의 쓰레드에서 이루어지도록 한다: DispatchQueue
  • upload
    • async로 시작한다: DispatchQueue.global(qos: .utility).async
    • multipartFormData를 실행하여 사용자로부터 data를 입력받는다. <- MultipartFormData
      • "Content-Type: multipart/form-data; boundary=alamofire.boundary.%08x%08x"
    • form data의 크기가 지정한 크기 보다 큰지 확인한다.(기본: 10MB)
      • 크지 않은 경우 메모리에서 바로 처리한다.
      • 큰 경우 데이타를 파일에 먼저 쓰고 이것을 서버에 보낸다.

SessionDelegate

  • URLSessionTask를 기준으로 하여 request를 저장하고 있는다.
  • NSObject를 상속한다.
  • extension으로 URLSessionDelegate, URLSessionTaskDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate, URLSessionStreamDelegate를 구현한다.
  • Requests credentials
    • NSURLAuthenticationMethodServerTrust이면 serverTrustPolicyManager로부터 등록된 serverTrustPolicy를 사용한다.
    • serverTrustPolicy가 유효하면 credential을 생성하여 넘겨준다.

ParameterEncoding

  • encode: URLRequestConvertible에 parameters를 포함한 URLRequest를 생성한다.
    • parameter는 percent-escaped, URL encoded query string으로 변환한다.

Request

  • URLSession과 RequestTask로 초기화 한다.
  • RequestTask의 종류(data, download, upload, stream)에 따라 관련 TaskDelegate를 갖는다.
  • authenticate
  • authorizationHeader
    • header에 추가하기 위한 것
    • Authorization: Basic 'base64 encoded of user:password'

DataRequest

  • session의 dataTask를 사용하여 URLRequest를 요청한다.
  • DataTaskDelegate와 연결된다.
  • extension으로 response 함수를 추가한다.

TaskDelegate

  • NSObject를 상속
    • objc에 의해 실행되는 함수들을 가지고 있다.
  • 사용자로부터의 response handler를 실행하기 위한 queue를 가지고 있는다.
    • OperationQueue
    • DataTaskDelegate, DownloadTaskDelegate, UploadTaskDelegate

Response

  • DefaultDataResponse: data나 upload의 non-serialized response에 사용
  • DataResponse<Value>: data나 upload의 serialized response에 사용
  • DefaultDownloadResponse: download의 non-serialized response에 사용
  • DownloadResponse<Value>: download의 serialized response에 사용
  • 애플이 제공하는 'URL Session Task Metrics'의 사용
    • 위의 모든 response에 추가로 Response 프로토콜을 정의하여 metric을 내부에 저장한다.

ResponseSerialization

  • extension의 형태로 서버로부터 온 데이타를 serialize해주는 함수들을 정의한다.
    • Raw data -> no serialize
    • Data -> serializeResponseData
    • String -> serializeResponseString
    • JSON -> serializeResponseJSON
    • Property List -> serializeResponsePropertyList
  • 내부에 저장할 타입과 이를 만들 serialize 함수를 가지고 있는 DataResponseSerializerProtocol 프로토콜을 정의한다.
    • DataResponseSerializerProtocol를 구현한 구조체로 DataResponseSerializer<Value>를 정의한다.
    • 특정 serialize 함수를 사용할 response 함수를 정의한다: responseJSON, responseString 등...
  • JSON을 응답으로 주는 경우에 대한 코드를 살펴보자.
    • JSON의 serializer로 DataRequest.jsonResponseSerializer를 사용한다.
    • DataRequest.jsonResponseSerializer는 'struct DataResponseSerializer<Any>'를 리턴하는데 이 struct는 DataResponseSerializerProtocol을 구현하고 있다.
    • DataResponseSerializerProtocol은 serialize의 결과를 리턴하는 serializeResponse 함수(closure)를 가지고 있다.
    • DataResponseSerializer는 serializeResponse 함수로 Request.serializeResponseJSON를 사용한다.
    • serializeResponseJSON는 JSONSerialization.jsonObject를 사용해서 응답 data로부터 object를 생성한다.
  • 내가 새로운 serializer를 추가한다면 DataResponseSerializerProtocol를 상속한 struct를 만들고 이를 response 함수에 넘겨주면 된다.

Timeline

  • Timeline
    • Alamofire 자체에서 추적하는 값
    • startTime: request 시작시 CFAbsoluteTimeGetCurrent()로 저장
    • requestCompletedTime: request 시작시 delegate.queue에 endTime을 계산하는 함수를 추가해주어 응답 시간이 바로 계산되어 저장되도록 한다.
    • initialResponseTime: delegate의 urlSession이 호출되는 시점에 값을 계산해준다.
    • serializationCompletedTime: completionHandler에서 serialization을 마친 후의 값.
    • latency = initialResponseTime - requestStartTime
    • requestDuration = requestCompletedTime - requestStartTime
    • serializationDuration = serializationCompletedTime - requestCompletedTime
    • totalDuration = serializationCompletedTime - requestStartTime
    • CustomStringConvertible과 CustomDebugStringConvertible을 extension으로 구현.
  • URL Session Task Metrics
    • iOS와 tvOS 10과 macOS 10.12에서 사용가능한 URLSessionTaskMetrics API.
    • SessionDelegate에 didFinishCollectingMetrics의 경우 불리는 urlSession을 정의한다: metric을 TaskDelegate의 metrics에 넘긴다.
    • completionHandler가 불릴 때 delegate로부터의 metrics를 response에 넘긴다: response의 _metrics에 metric이 저장이 된다.
    • 사용자가 response.metrics를 부르면 _metrics를 리턴한다.

ServerTrustPolicy

  • ServerTrustPolicyManager에 의해 호스트 별로 관리될 수 있다.
  • URLSession에 ServerTrustPolicyManager를 저장해 놓기 위해 AssociatedKeys를 사용한다.
  • 5가지의 policy가 있다: performDefaultEvaluation, pinCertificates, pinPublicKeys, disableEvaluation, customEvaluation
  • SessionDelegate에서 서버로부터 authentication관련 응답이 왔을 때 사용된다.

MultipartFormData

  • 서버에 보낼 내용을 BodyPart에 저장한다.
  • append: Data, InputStream, URL의 형태로 추가한다.
  • 파일의 URL을 주는 경우
    • mimetype을 사용자가 지정해 주지 않으면 mimetype을 확인한다.
      • content type을 가져올 수 있으면 가져오고 안되면 "application/octet-stream"으로 한다.
      • content type가져올 때 사용하는 함수: UTTypeCreatePreferredIdentifierForTag -> UTTypeCopyPreferredTagWithClass
    • 파일이 올바른지를 위해 아래의 5가지를 확인한다.
      • is file URL?
      • is file URL reachable?
      • is file URL a directory?
      • can the file size be extracted?
      • can a stream be created from file URL?
  • BodyPart
    • HTTPHeaders: [String: String]
    • InputStream
  • encode
    • initialData
      • "--(boundary)(EncodingCharacters.crlf)"
    • headerData
      • Content-Disposition: form-data; name="file1"; filename="a.txt"
      • Content-Type: text/plain
      • 마지막에 EncodingCharacters.crlf 를 추가한다.
    • bodyStreamData
    • finalBoundaryData
      • "(EncodingCharacters.crlf)--(boundary)--(EncodingCharacters.crlf)"
  • writeEncodedData
    • 서버에 보낼 내용이 큰 경우에는 파일로 저장한 후 보낼 수 있도록 한다.

RequestAdapter

  • public protocol RequestAdapter
    • adapt 함수를 구현하도록 한다.
  • URLRequest를 변경하여 변경된 Request를 서버에 요청하도록 한다.
  • SessionManager에서 adapter를 로컬 변수로 들고 있다. 따라서, 여기에 내가 원하는 adapter를 구현한 후 이것을 SessionManager의 adapter에 설정해 준다.
  • URLRequest에 extension으로 adapt 함수를 구현한다. 이 함수는 RequestAdapter를 input으로 받아서 RequestAdapter의 adapt 함수를 호출해준다.
  • 동작 방법
    • SessionManager의 request 함수를 호출한다.
    • DataRequest의 Requestable의 task 함수를 통해 URLSessionTask를 생성한다.
    • task 함수의 input으로 RequestAdapter를 넣는다.
    • task 함수는 URLSessionTask를 생성하기 전에 URLRequest의 adapt를 먼저 호출한다.
    • adapt 함수로부터 리턴받은 URLRequest를 URLSessionTask를 생성할 때 input으로 넣어준다.

RequestRetrier

  • 실행중 에러가 발생했을 때 다시 시도할 수 있도록 한다.
  • public protocol RequestRetrier
    • should 함수를 구현하도록 한다. -> 사용자가 필요한 작업을 한 후 completion을 호출하도록 한다.
  • Request에 retryCount가 로컬 변수로 있어서 retry시 값을 증가시켜준다. 따라서, should 함수 구현시 retryCount 값을 통해 몇번이나 다시 시도 중인지 파악할 수 있다.(예를 들어 무한 시도를 막는 용도로 사용할 수 있다.)
  • 동작 방법
    • SessionManager의 request 함수를 호출한다.
    • 에러 발생시 allowRetrier 함수를 호출한다.
    • DispatchQueue.global의 qos를 utility로 한 asynchronous로 RequestRetrier의 should 함수를 호출한다.
    • should 함수에서의 completion은 input으로 다시 시도를 할지 안할지를 받아서 다시 시도 할 거면 URLSessionTask를 만들어서 실행하는 작업을 다시 시도하고 아니면 끝낸다.

Response시 실행되는 handler의 동작

  • TaskDelegate에 OperationQueue를 설정한다.
    • maxConcurrentOperationCount = 1로 한번에 하나씩 순서대로 실행되도록 한다.
    • isSuspended = true로 중단된 상태로 둔다.
    • qualityOfService = utility로 한다.
  • request에 response handler를 등록하면 OperationQueue에 관련된 operation을 하는 함수를 등록하는데 보통 이 함수의 끝에서 response handler를 호출해 준다.
    • 사용자가 별도로 queue를 설정해 주지 않았으면 보통 DispatchQueue.main에서 handler를 실행한다.
  • 서버로부터 응답이 오거나 request 중 에러가 발생하면 isSuspended를 false로 설정하여 등록된 operation들이 순차적으로 실행되도록 한다.

댓글 없음:

댓글 쓰기

Building asynchronous views in SwiftUI 정리

Handling loading states within SwiftUI views self loading views View model 사용하기 Combine을 사용한 AnyPublisher Making SwiftUI views refreshable r...