Notice
Recent Posts
Recent Comments
Link
관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] 4. Alamofire 사용 방법 - 로그 Log (EventMonitor) 본문

iOS 응용 (swift)

[iOS - swift] 4. Alamofire 사용 방법 - 로그 Log (EventMonitor)

jake-kim 2021. 10. 16. 16:11

1. Alamofire 사용 방법 - Network Layer 구현 (Moya 프레임워크처럼 사용하는 방법)

2. Alamofire 사용 방법 - 토큰 갱신 방법1 (Interceptor, adapt, retry)

3. Alamofire 사용 방법 - 토큰 갱신 방법2 (AuthenticationCredential, Authenticator, AuthenticationInterceptor)

4. Alamofire 사용 방법 - 로그 Log (EventMonitor)


EventMonitor

  • request, response 시점에 불리는 메소드가 들어있는 프로토콜
  • 구현해야할 주된 프로퍼티, 메소드는 3가지

queue 프로퍼티

/// The `DispatchQueue` onto which Alamofire's root `CompositeEventMonitor` will dispatch events. `.main` by default.
var queue: DispatchQueue { get }
  • 순서대로 로깅이 이루어질 수 있도록 모든 이벤트를 queue에 담기위해 queue프로퍼티 필요
class APIEventLogger: EventMonitor {

    let queue = DispatchQueue(label: "myNetworkLogger")

}

requestDidFinish(_:) 메서드

/// Event called when a `Request` finishes and response serializers are being called.
func requestDidFinish(_ request: Request)
  • request가 끝나고 response가 시작될때 호출
class APIEventLogger: EventMonitor {
...
  func requestDidFinish(_ request: Request) {
    print("🛰 NETWORK Reqeust LOG")
    print(request.description)
    
    print(
      "URL: " + (request.request?.url?.absoluteString ?? "")  + "\n"
        + "Method: " + (request.request?.httpMethod ?? "") + "\n"
        + "Headers: " + "\(request.request?.allHTTPHeaderFields ?? [:])" + "\n"
    )
    print("Authorization: " + (request.request?.headers["Authorization"] ?? ""))
    print("Body: " + (request.request?.httpBody?.toPrettyPrintedString ?? ""))
  }
}

request<Value>(_:didParseResponse:)

/// Event called when a `DataRequest` calls a `ResponseSerializer` and creates a generic `DataResponse<Value, AFError>`.
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>)
  • 응답을 받은 경우 호출
class APIEventLogger: EventMonitor {
...
    func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
        print("🛰 NETWORK Response LOG")
        print(
          "URL: " + (request.request?.url?.absoluteString ?? "") + "\n"
            + "Result: " + "\(response.result)" + "\n"
            + "StatusCode: " + "\(response.response?.statusCode ?? 0)" + "\n"
            + "Data: \(response.data?.toPrettyPrintedString ?? "")"
        )
    }
}
  • APIEventLogger 전체 코드
import Alamofire

class APIEventLogger: EventMonitor {

    let queue = DispatchQueue(label: "myNetworkLogger")

    func requestDidFinish(_ request: Request) {
      print("🛰 NETWORK Reqeust LOG")
      print(request.description)

      print(
        "URL: " + (request.request?.url?.absoluteString ?? "")  + "\n"
          + "Method: " + (request.request?.httpMethod ?? "") + "\n"
          + "Headers: " + "\(request.request?.allHTTPHeaderFields ?? [:])" + "\n"
      )
      print("Authorization: " + (request.request?.headers["Authorization"] ?? ""))
      print("Body: " + (request.request?.httpBody?.toPrettyPrintedString ?? ""))
    }

    func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
        print("🛰 NETWORK Response LOG")
        print(
          "URL: " + (request.request?.url?.absoluteString ?? "") + "\n"
            + "Result: " + "\(response.result)" + "\n"
            + "StatusCode: " + "\(response.response?.statusCode ?? 0)" + "\n"
            + "Data: \(response.data?.toPrettyPrintedString ?? "")"
        )
    }
}

extension Data {
    var toPrettyPrintedString: String? {
        guard let object = try? JSONSerialization.jsonObject(with: self, options: []),
              let data = try? JSONSerialization.data(withJSONObject: object, options: [.prettyPrinted]),
              let prettyPrintedString = NSString(data: data, encoding: String.Encoding.utf8.rawValue) else { return nil }
        return prettyPrintedString as String
    }
}

Session을 새로 생성

  • session객체를 만들때 eventMonitors 객체를 주입해주기 위해서 session객체를 새로 생성
  • request를 요청할 땐 보통 AF.request()로 사용하지만, session객체를 이용
  • 기존의 AF는 Session.default를 보고있는 형태이므로 session을 새로만들어서 사용해도 무방

  • Session생성
class API {
    static let session: Session = {
        let configuration = URLSessionConfiguration.af.default
        let apiLogger = APIEventLogger()
        return Session(configuration: configuration, eventMonitors: [apiLogger])
    }()
}
  • API호출 정의: AF.request가 아닌 위에서 정의한 API.session.request()로 접근
// PredictAgeAPI.swift

/// 이름과 패스워드로 로그인 + EventLogger 이용
static func predictWithEventLogger(request: PredictAgeRequest, completion: @escaping (_ succeed: Person?, _ failed: Error?) -> Void) {
    API.session.request(PredictAgeTarget.predict(request), interceptor: MyRequestInterceptor())
        .responseDecodable { (response: AFDataResponse<PredictAgeResponse>) in
            switch response.result {
            case .success(let response):
                completion(response.toDomain, nil)
            case .failure(let error):
                completion(nil, error)
            }
        }
}

사용

@IBAction func didTapCallAPIButton(_ sender: Any) {
    let request = PredictAgeRequest(name: textField.text ?? "")
    PredictAgeAPI.predictWithEventLogger(request: request) { [weak self] succeed, failed in
        guard let succeed = succeed else { return }
        self?.label.text = "\(succeed.name)이름, 예측 = \(succeed.age)살"
    }
}
  • API호출 시 로그 결과

* 전체 소스 코드

https://github.com/JK0369/ExAlamofire

* 참고

https://alamofire.github.io/Alamofire/Protocols/EventMonitor.html

Comments