안녕하세요.
저번엔 TCA를 활용하여 아주 간단한 예시를 만들어봤습니다.
정확하게는 TCA를 사용해서 사용자 Action 이벤트를 통해 State 값을 동기적으로 바꾸고 바뀐 값이 UI에 반영시키는 것을 경험해 봤어요.
제가 '동기적'이란 말에 강조를 했죠??
'동기'하면 동시에 '비동기'가 떠오르지 않나요??ㅎㅎ..
맞습니다.
이번에는 TCA를 사용해서 비동기 API를 호출하는 방법에 대해서 알아보고 간단한 예시를 만들어볼게요.
(TCA 0.54.0 기준으로 작성했으며, 전체 코드는 여기를 봐주세요!)
TCA로 비동기 동작을 하고 싶을 때는 run과 TaskResult를 기억해야 합니다.
저번 TCA 예제를 다시 돌이켜보면, State 값을 (동기적으로) 변경하고 다른 추가 동작이 필요 없을 때 뭘 했죠??
EffectTask.none을 반환했습니다.
하지만 비동기 동작이 필요한 경우에는 EffectTask.run을 반환해줘야 합니다.
그리고 run과 세트로 자주 사용되는 것이 TaskResult라는 타입이에요.
(TaskResult는 TCA에서 정의한 타입입니다.)
TaskResult는 Swift의 Result 타입과 동일하게 enum 타입이며 success, failure 중 하나의 값을 가집니다.
async 함수의 결과값 또는 Swift의 Result값을 TaskResult 타입으로 쉽게 변환할 수 있습니다.
Swift Result 타입과의 차이점은 success case에 대한 결과 타입만 정의하고 failure의 경우 구체적인 Error 타입을 설정할 필요가 없습니다.
(failure case의 경우 untyped Error로 유지됩니다.)
run과 TaskResult를 사용해서 예시를 만들어볼게요.
# 예제
버튼을 누르면 REST API를 통해 랜덤한 문자열을 전달받고 화면에 출력하는 예제를 만들어볼게요.
랜덤한 문자열을 전달받는 async 함수를 만들었어요. 이 함수는 Reducer에서 호출될 예정입니다.
(TCA의 비동기 처리는 async 함수만 사용할 수 있기 때문에, callback 형태가 아닌 async로 만들어줬습니다.)
View도 만들어줄게요.
View에서 필요한 State, Action, reduce를 정의한 기본 Reducer도 만들어줄게요.
(여기에 코드를 계속 추가해 줄 거예요.)
지금 구현해야 하는 것이 크게 3가지 있습니다.
- 비동기 함수 호출하기 전 indicator 표시. 비동기 함수 끝나면 indicator 숨기기.
- buttonTapped Action이 들어왔을 때 비동기 함수 호출하기.
- 비동기 함수 호출 결과값을 State에 반영.
밑에서부터 작업하는 게 수월할 것 같네요. 비동기 함수 호출이 끝나고 나서 후처리 하는 부분을 먼저 구현해 볼게요.
'후처리'이기 때문에 비즈니스 로직에 해당합니다. 즉, 추가 Action을 정의해줘야 한다는 것이죠.
일반적으로 TCA는 비동기 함수 호출의 결과값을 TaskResult 타입으로 변경해서 Action의 인자값으로 넘겨주는 방식으로 비동기 함수 결과를 처리합니다.
TaskResult 타입을 인자로 받기 때문에, .success, .failure로 case를 나눌 수 있어요.
다음으로 buttonTapped Action에서 비동기 함수를 호출해 볼게요.
buttonTapped Action이 발생하면 비동기 함수를 호출할 것이기 때문에 .none 대신 .run을 반환해줘야 합니다.
그리고 send를 사용하면 다른 Action을 명시적으로 발생(emit)시킬 수 있어요.
그래서 비동기 함수 호출이 끝난 결과값을 TaskResult 타입으로 바꾸고, send를 사용해서 비동기 함수 후처리 Action을 바꾼 TaskResult 타입과 함께 emit 시켜주는 것이죠.
마지막으로 indicator 표시 여부를 설정해 줍시다.
reduce 함수에선 state가 inout으로 설정되어 있기 때문에 concurrency closure인 run 안에서는 state를 사용할 수 없습니다.
(만약 사용하려고 하면 이런 에러가 발생해요.)
그렇기 때문에, indicator 표시 여부를 수정하는 추가 Action을 정의해 주고 send로 Action을 emit 하는 방식으로 구현해줘야 합니다.
(async 동작뿐만 아니라 sync 동작도 send로 Action을 emit 시킬 수 있습니다.)
끝!!
쉬우면서도 어렵고, 간단하면서도 복잡한 게 TCA인 것 같아요..
오늘 살펴본 내용은 꼭 알고 넘어가면 좋을 것 같습니다.
# 요약
- 비동기 함수 호출이 필요하면 run + TaskResult를 사용하자.
- 다른 Action을 emit 하고 싶을 땐 send를 사용하자.
- TCA 맛있네.
# 참고
이번 글은 여기서 마무리.
'SwiftUI' 카테고리의 다른 글
navigationBar가 숨김 처리된 상태에서 제스처로 화면 뒤로가기 (0) | 2023.06.28 |
---|---|
TCA(3) : _printChanges (0) | 2023.06.24 |
TCA(1) : ReducerProtocol, StoreOf, WithViewStore (0) | 2023.06.13 |
TCA(0) : The Composable Architecture 개요 (0) | 2023.06.07 |
SwiftUI View Lifecycle에 대해 알아보자. (0) | 2023.05.28 |