[TCA] Effect #1 (LongLiving)
목차
- LongLiving에 대한 설명
- Effect LongLiving 예제 살펴보기
# LongLiving에 대한 설명
이번에는 NotificationCenter의 알림들처럼 effect의 라이프사이클이 긴 것들을 처리하는 방법과 그것들을 View의 라이프 사이클과 연결하는 방법에 대해서 알아보고자 함.
이번에는 스크린샷을 여러번 찍으면서 UI의 카운트가 몇번이나 발생하는지 관찰하는 예제
그런 다음 다른 화면으로 이동하여 스크린샷을 촬영한 후 이 화면에서 스크린샷이 *not* 카운트되는지 확인하고, 해당 화면을 떠나면 노티피케이션 효과가 자동으로 취소되고 화면에 들어갈 때 다시 시작.
# Effect LongLiving 예제 살펴보기
import ComposableArchitecture
import SwiftUI
import XCTestDynamicOverlay
// MARK: - Feature domain
struct LongLivingEffects: Reducer {
struct State: Equatable {
var screenshotCount = 0
}
enum Action: Equatable {
case task
case userDidTakeScreenshotNotification
}
@Dependency(\.screenshots) var screenshots
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .task:
// 뷰 onAppear 타이밍부터 스크린샷을 찍을 때 발생하는 effect 시작
return .run { send in
for await _ in await self.screenshots() {
await send(.userDidTakeScreenshotNotification)
}
}
case .userDidTakeScreenshotNotification:
state.screenshotCount += 1
return .none
}
}
}
extension DependencyValues {
var screenshots: @Sendable () async -> AsyncStream<Void> {
get { self[ScreenshotsKey.self] }
set { self[ScreenshotsKey.self] = newValue }
}
}
private enum ScreenshotsKey: DependencyKey {
// 이런 방식으로 노티피케이션을 등록할 수 있음.
static let liveValue: @Sendable () async -> AsyncStream<Void> = {
await AsyncStream(
NotificationCenter.default
.notifications(named: UIApplication.userDidTakeScreenshotNotification)
.map { _ in }
)
}
static let testValue: @Sendable () async -> AsyncStream<Void> = unimplemented(
#"@Dependency(\.screenshots)"#, placeholder: .finished
)
}
// MARK: - Feature view
struct LongLivingEffectsView: View {
let store: StoreOf<LongLivingEffects>
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
Form {
Section {
AboutView(readMe: readMe)
}
Text("A screenshot of this screen has been taken \(viewStore.screenshotCount) times.")
.font(.headline)
Section {
NavigationLink(destination: self.detailView) {
Text("Navigate to another screen")
}
}
}
.navigationTitle("Long-living effects")
.task { await viewStore.send(.task).finish() } // finish는 작업이 완료될 때까지 대기
// finish가 없을 경우에는 binding 중첩과 동일한 현상 발생
// NavigationLink로 뷰를 아예 다 덮어 쓴 경우에는 task가 외부에서 컨트롤 하더라도 detailView에서 스크린 샷 액션이 불리지 않음
}
}
var detailView: some View {
Text(
"""
Take a screenshot of this screen a few times, and then go back to the previous screen to see \
that those screenshots were not counted.
"""
)
.padding(.horizontal, 64)
.navigationBarTitleDisplayMode(.inline)
}
}
// MARK: - SwiftUI previews
struct EffectsLongLiving_Previews: PreviewProvider {
static var previews: some View {
let appView = LongLivingEffectsView(
store: Store(initialState: LongLivingEffects.State()) {
LongLivingEffects()
}
)
return Group {
NavigationView { appView }
NavigationView { appView.detailView }
}
}
}
시뮬레이터로 테스트하려면 Device - Trigger Screenshot을 눌러야 확인할 수 있다.
NotificationCenter를 해당 TCA에서 사용하는 방법에 대해서 이런 방식으로도 컨트롤 할 수 있구나를 알게 되었는데, 해당 부분은 쿠링 프로젝트에 적용하면서 어떻게 구성할지 더 고민해보자!
'apple > TCA' 카테고리의 다른 글
[TCA] Effect #5 (Timers) (0) | 2023.10.07 |
---|---|
[TCA] Effect #4 (Refreshable) (0) | 2023.10.07 |
[TCA] Effect #2 (Cancellation) (0) | 2023.10.07 |
[TCA] Effect #1 (Basics) (1) | 2023.10.07 |
[TCA] SharedState (1) | 2023.09.27 |