EEYatHo 앱 깎는 이야기

SwiftUI ) TCA 단점 ( with AsyncStream, cancel, cancelable ) - EEYatHo iOS 본문

iOS, Swift/Tip, Bug, Swift Error

SwiftUI ) TCA 단점 ( with AsyncStream, cancel, cancelable ) - EEYatHo iOS

EEYatHo 2024. 2. 1. 18:35
반응형

 

TCA의 단점


WebSocket 을 구현하는 등,

Stream을 받아와서 구독하고, 지속적으로 이벤트를 수신해야할 때 RetainCycle 이 무조건 발생한다.

즉, view의 사이클에 맞춰서 명시적으로 구독을 해제해줘야 한다.

 

이유

Action을 발생할 수 있는 엔드포인트를 .run을 통해 받아오고,

Stream의 실행부에, 엔드포인트를 넣기 때문에 RetainCycle이 발생한다.

( 스트림실행부 -> 엔드포인트 -> 리듀서 -> 스트림 -> 스트림실행부 )

( Class의 weak가 그립다.. )

 

 

결과

때문에, view의 사이클에 맞춰서 명시적으로 구독을 해제해줘야, reducer도 잘 해제된다.

( onAppear, onDisappear 을 활용. )

( 만약 init, deinit 으로 하고싶다면, class로 DeinitDetector를 만들어서 사용. )

 

 

 

Rx나 Combine으로 시도


Rx/Combine의 체이닝 마지막에 넣는 diposeBag/cancelables를 사용할 때 문제가 발생.

 

 

Rx, Combine 공통 제약사항

diposeBag/cancelables 의 선언 위치는 리듀서의 프로퍼티가 아닌, State안에 넣어야함.

( 리듀서는 struct이고, reduce 함수는 mutating func가 아니므로, 리듀서의 프로퍼티를 변경할 수 없음. )

( 때문에, 명시적으로 해제하기 위해서는 State 안에 넣어야함 )

 

 

Combine의 제약사항 -> 불가능

cancelables는 퍼블리셔의 store에 넣기위해서는 mutable해야하는데,

mutable한 채로 .run 클로저 안에 가져올 수 가 없음..

 

 

Rx의 제약사항 -> 코드가 더러워짐

1. disposeBag는 Equatable를 따르지 않기에 State에 넣으면 == 함수를 구현해야함.

2. 엔드포인트가 Sendable라서 실행부 내부를 Task 로 감싸야함.

 

 

답은 AsyncStream -> Best Practice!

Task를 State에 들고있고, 수동으로 해제했지만 사실 그럴필요도 없음.

이를 지원하는 Effect가 존재하기 때문.

( cancel, cancelable )

 

 

자세한 구현은 TCA 깃헙의 Example 중 웹소켓 구현 부분을 참고

https://github.com/pointfreeco/swift-composable-architecture/blob/main/Examples/CaseStudies/SwiftUICaseStudies/02-Effects-WebSocket.swift

 

 

이걸 처음부터 알았다면 내 하루를 아낄 수 있었을텐데..

Comments