HIT해
[iOS/SwiftUI] TCA API 통신 구현하기 with AF ( TCA 1.12.1 ) 본문
728x90
깃허브의 Example_Search 예제를 보고 구현을 해보자.
struct Peperoni: Decodable, Equatable, Sendable {
var message : String
}
@DependencyClient
struct TmpClient {
var regist: @Sendable () async throws -> Peperoni
}
// 실제 통신 전 테스트
extension TmpClient: TestDependencyKey {
// 여기서의 Self는 TmpClient
static let previewValue = Self()
static let testValue = Self()
}
extension DependencyValues {
var tmpClient: TmpClient {
get { self[TmpClient.self] }
set { self[TmpClient.self] = newValue }
}
}
extension TmpClient: DependencyKey {
static let liveValue = TmpClient(
regist: {
var components = URLComponents(string: "http://...")!
components.queryItems = [URLQueryItem(name: "name", value: query)]
let (data, _) = try await URLSession.shared.data(from: components.url!)
return try JSONDecoder().decode(Peperoni.self, from: data) //
}
)
}
URLSession으로 간단하게 구현된 예제다.
오류가 났을때의 처리를 TCA Reducer에서 처리해주기에 따로 catch 구문이 없는 것을 볼 수 있다.
해당 작업에 대해 Reducer에서 다음과 같은 설정들이 있어야한다.
- 통신 Client 불러오기
- 응답 값으로 Result<디코딩타입, Error> 받는 액션
- 2번 액션 성공, 실패에 따른 동작
간단하게 코드로 살펴보자
enum Action: BindableAction {
case clickMessage
case tmpResponse(Result<Peperoni, Error>)
}
...
case .clickMessage:
return .run { send in
await send(.tmpResponse(Result{ try await self.tmpClient.regist() }))
}
// 실패했을때
case .tmpResponse(.failure):
print("통신오류")
return .none
// 성공했을때 디코딩 값을 바로 리듀서 상태에 반영
case let .tmpResponse(.success(response)):
state.message = response.message
return .none
이런 구조로 만들 수 있다.
이걸 AF로 바꿔보자.
공통적으로 사용할 수 있는 AF 코드를 유틸에 따로 뺴주고.
//
// apiFetch.swift
// DolHaruBang
//
// Created by 양희태 on 9/3/24.
//
import Alamofire
import Foundation
func fetch<T: Decodable>(
url: String,
model: T.Type,
method: HTTPMethod ,
queryParameters: [String: String]? = nil,
headers: HTTPHeaders? = nil,
body: Data? = nil
) async throws -> T {
return try await withCheckedThrowingContinuation { continuation in
// URLRequest 객체 생성
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = method.rawValue
// 쿼리 파라미터 추가
if let queryParameters = queryParameters {
var components = URLComponents(url: request.url!, resolvingAgainstBaseURL: false)!
components.queryItems = queryParameters.map { URLQueryItem(name: $0.key, value: $0.value) }
request.url = components.url
}
// 헤더 추가
if let headers = headers {
request.headers = headers
}
// 바디 데이터 추가 (POST 및 PUT 요청 시 사용)
if (method == .post || method == .put), let body = body {
request.httpBody = body
}
// Alamofire 요청
AF.request(request)
.validate() // 응답 상태 코드 검증
.responseData { response in
switch response.result {
case .success(let data):
do {
let jsonDecoder = JSONDecoder()
let decodedModel = try jsonDecoder.decode(T.self, from: data)
continuation.resume(returning: decodedModel)
} catch {
continuation.resume(throwing: error)
}
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
}
사용할때는 간단하게 사용할 수 있다.
extension TmpClient: DependencyKey {
static let liveValue = TmpClient(
regist: {
let url = "http://..."
// let queryParameters: [String: String] = ["param1": "value1"] // 필요에 따라 설정
// let headers: HTTPHeaders = ["Authorization": "Bearer token"] // 필요에 따라 설정
// return try await fetch(url, model: Peperoni.self, queryParameters: queryParameters, headers: headers)
return try await fetch(url: url, model: Peperoni.self, method: .get)
}
)
}
'Swift > Swift 개발 노트' 카테고리의 다른 글
[Swift/Xcode] Multiple commands produce 해결하기 ( feat Tuist ) (0) | 2024.09.20 |
---|---|
[Swift/TCA] TCA를 활용한 테스트코드 작성 (TCA ver 1.12.1) (0) | 2024.09.20 |
[iOS/SceneKit] 3D 화면에 텍스트 출력하기 (0) | 2024.09.04 |
[iOS/SceneKit] 특정 노드 액션 구현하기 (0) | 2024.09.03 |
[iOS/SwiftUI] TCA NavigationStack 개발하기 (1.12.1) (0) | 2024.09.02 |