[Swift] Alamofire를 Moya처럼 사용해보자! By Router Pattern (1편 - Foundation Setting)
[Swift] Alamofire를 Moya처럼 사용해보자! By Router Pattern (2편 - Services, Routers 구현)
3편까지는 다양한 request 타입이나 task의 종류에 따른 대응을 하는 과정이었습니다.
이번 편에서는 개발 과정에서 Network 통신의 상태를 편하게 확인할 수 있도록 하는 EventLogger를 제작해 보겠습니다.
EventMonitor 프로토콜
Alamofire에는 request나 response 시에 호출되는 메서드들을 정의해 놓은 EventMonitor라는 프로토콜이 있습니다.
/// Protocol outlining the lifetime events inside Alamofire. It includes both events received from the various
/// `URLSession` delegate protocols as well as various events from the lifetime of `Request` and its subclasses.
public protocol EventMonitor {
해석해보면 알라모파이어 내에 존재하는 생활주기를 추적할 수 있게 만든 프로토콜이라고 하는데요, URLSession delegate 프로토콜에서 온 이벤트나 Request의 생활주기에서 온 이벤트들을 포함하여 받는다고 합니다.
request가 생성되거나, response가 왔거나, request가 취소되었거나 하는 상황에 실행되는 메서드들이 존재하기 때문에, 그 메서드 안에 원하는 처리를 해주면 log를 띄우거나 error 처리를 할 수 있습니다.
아래는 EventMonitor를 채택하여 구현한 APIEventLogger 클래스입니다.
import Alamofire
import Foundation
class APIEventLogger: EventMonitor {
let queue = DispatchQueue(label: "NetworkLogger")
func requestDidFinish(_ request: Request) {
print("----------------------------------------------------\n\n" + " 🛰 NETWORK Reqeust LOG\n" + "\n----------------------------------------------------")
print("1️⃣ URL / Method / Header" + "\n" + "URL: " + (request.request?.url?.absoluteString ?? "") + "\n"
+ "Method: " + (request.request?.httpMethod ?? "") + "\n"
+ "Headers: " + "\(request.request?.allHTTPHeaderFields ?? [:])")
print("----------------------------------------------------\n2️⃣ Body")
if let body = request.request?.httpBody?.toPrettyPrintedString {
print("Body: \(body)")
} else { print("보낸 Body가 없습니다.")}
print("----------------------------------------------------\n")
}
func request<Value>(_ request: DataRequest, didParseResponse response: DataResponse<Value, AFError>) {
print(" 🛰 NETWORK Response LOG")
print("\n----------------------------------------------------")
switch response.result {
case .success(_):
print("3️⃣ 서버 연결 성공")
case .failure(_):
print("3️⃣ 서버 연결 실패")
print("올바른 URL인지 확인해보세요.")
}
print("Result: " + "\(response.result)" + "\n"
+ "StatusCode: " + "\(response.response?.statusCode ?? 0)"
)
if let statusCode = response.response?.statusCode {
switch statusCode {
case 400..<500:
print("❗오류 발생 : RequestError\n" + "잘못된 요청입니다. request를 재작성 해주세요.")
case 500:
print("❗오류 발생 : ServerError\n" + "Server에 문제가 발생했습니다.")
default:
break
}
}
print("----------------------------------------------------")
print("4️⃣ Data 확인하기")
if let response = response.data?.toPrettyPrintedString {
print(response)
} else { print("❗데이터가 없거나, Encoding에 실패했습니다.")}
print("----------------------------------------------------")
}
func request(_ request: Request, didFailTask task: URLSessionTask, earlyWithError error: AFError) {
print("URLSessionTask가 Fail 했습니다.")
}
func request(_ request: Request, didFailToCreateURLRequestWithError error: AFError) {
print("URLRequest를 만들지 못했습니다.")
}
func requestDidCancel(_ request: Request) {
print("request가 cancel 되었습니다")
}
}
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
}
}
requestDidFinish : 말 그대로 request가 끝났을 때 호출됩니다. 파라미터 request에 접근하여 header, body, method 등을 확인할 수 있습니다.
request<Value>(_:didParseResponse:) : response가 오면 호출됩니다. response의 결과에 따라 통신 결과를 요약해서 확인할 수 있습니다.
Custom Session에 Logger 할당해주기
이렇게 EventLogger를 만들었으니, 기존에 BaseService에 만든 AFManager에 EventLogger를 할당해주기만 하면, AFManager를 통해 실행되는 모든 request에서 동일한 EventLogger를 적용할 수 있습니다.
class BaseService {
let AFManager: Session = {
var session = AF
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = NetworkEnvironment.requestTimeOut
configuration.timeoutIntervalForResource = NetworkEnvironment.resourceTimeOut
let eventLogger = APIEventLogger()
session = Session(configuration: configuration, eventMonitors: [eventLogger])
return session
}()
}
아래는 실행 결과입니다.
----------------------------------------------------
🛰 NETWORK Reqeust LOG
----------------------------------------------------
1️⃣ URL / Method / Header
URL: "-"
Method: GET
Headers: ["accesstoken": "", "Content-Type": "Application/json"]
----------------------------------------------------
2️⃣ Body
보낸 Body가 없습니다.
----------------------------------------------------
🛰 NETWORK Response LOG
----------------------------------------------------
3️⃣ 서버 연결 성공
Result: success(1228 bytes)
StatusCode: 200
----------------------------------------------------
4️⃣ Data 확인하기
{
"status" : 200,
"success" : true,
"message" : "스케줄 조회 성공",
"data" : {
"onSale" : 0,
"category" : "도서\/티켓\/음반",
"content" : "새 책입니다.",
"isPriceSuggestion" : "가격제안가능",
"price" : "16,000원",
"title" : "최태성 한국사",
"image" : [
"-",
"-",
"-"
],
"view" : 3,
"isLiked" : false,
"createdAt" : "5분전",
"user" : {
"profile" : "-",
"name" : "Ussser",
"area" : "개봉동"
}
}
}
----------------------------------------------------
참고
'iOS' 카테고리의 다른 글
[iOS / Firebase] FireBase Remote config과 A/B Test (1편 - 개념 알아보기) (0) | 2022.06.11 |
---|---|
[Swift] Local Notification을 이용하여 유저에게 알림 보내기 (0) | 2022.06.01 |
[Swift] Alamofire를 Moya처럼 사용해보자! By Router Pattern (3편 - body, queryBody, requestPlain, Multipart 구현) (0) | 2022.05.31 |
[iOS] AppDelegate.Swift는 무슨 역할을 하는 것일까? (3) | 2022.05.20 |
[Swift] Coordinator Pattern 기본!! With RayWanderlich Tutorial! (0) | 2022.05.19 |