apple/TCA

[TCA] Effect #4 (Refreshable)

lgvv 2023. 10. 7. 18:30

[TCA] Effect #4 (Refreshable)

 

목차

 - Refreshable에 대한

 - Refreshable을 사용하는 예제

 

# Refreshable에 대한

 

이번에는 List를 리프레시 하는 방법에 대해서 알아보자

이번에는 버튼을 클릭해서 API 콜하는게 아니라 리프레시를 통해 API를 콜 할 예정이다.

 

특이점으로는 viewStore.send(..., animation: ) 으로 animation을 건네주어 볼 예정!

 

# Refreshable을 사용하는 예제

// MARK: - Feature domain

struct Refreshable: Reducer {
    struct State: Equatable {
        var count = 0
        var fact: String?
    }
    
    enum Action: Equatable {
        case cancelButtonTapped
        case decrementButtonTapped
        case factResponse(TaskResult<String>)
        case incrementButtonTapped
        case refresh
    }
    
    @Dependency(\.factClient) var factClient
    private enum CancelID { case factRequest }
    
    func reduce(into state: inout State, action: Action) -> Effect<Action> {
        switch action {
        case .cancelButtonTapped:
            return .cancel(id: CancelID.factRequest)
            
        case .decrementButtonTapped:
            state.count -= 1
            return .none
            
        case let .factResponse(.success(fact)):
            state.fact = fact
            return .none
            
        case .factResponse(.failure):
            // 여기서 에러 핸들링!
            return .none
            
        case .incrementButtonTapped:
            state.count += 1
            return .none
            
        case .refresh:
            state.fact = nil
            return .run { [count = state.count] send in
                await send(
                    .factResponse(TaskResult { try await self.factClient.fetch(count) }), // API 콜!
                    animation: .default // 애니메이션 지정 가능
                )
            }
            .cancellable(id: CancelID.factRequest)
        }
    }
}

// MARK: - Feature view

struct RefreshableView: View {
    @State var isLoading = false
    let store: StoreOf<Refreshable>
    
    var body: some View {
        WithViewStore(self.store, observe: { $0 }) { viewStore in
            List {
                Section {
                    AboutView(readMe: readMe)
                }
                
                HStack {
                    Button {
                        viewStore.send(.decrementButtonTapped)
                    } label: {
                        Image(systemName: "minus")
                    }
                    
                    Text("\(viewStore.count)")
                        .monospacedDigit()
                    
                    Button {
                        viewStore.send(.incrementButtonTapped)
                    } label: {
                        Image(systemName: "plus")
                    }
                }
                .frame(maxWidth: .infinity)
                .buttonStyle(.borderless)
                
                if let fact = viewStore.fact {
                    Text(fact)
                        .bold()
                }
                if self.isLoading {
                    Button("Cancel") {
                        viewStore.send(.cancelButtonTapped, animation: .default) // 애니메이션을 옵션으로 건네줄 수 있음.
                    }
                }
            }
            .refreshable {
                self.isLoading = true
                defer { self.isLoading = false }
                await viewStore.send(.refresh).finish()
            }
        }
    }
}

// MARK: - SwiftUI previews

struct Refreshable_Previews: PreviewProvider {
    static var previews: some View {
        RefreshableView(
            store: Store(initialState: Refreshable.State()) {
                Refreshable()
            }
        )
    }
}

 

애니메이션이 없으면 UI가 뚝 끊기듯이 나타난다!! 

애니메이션을 통해 더 나은 UX를 제공할 수 있을 거 같다!

비동기 send를 처리해야 하는 경우 finish를 사용해야 한다.

그렇지 않을 경우 바인딩 중첩 문제가 발생하는데, 자세한 부분은 한번 싹 공부하고나서 다시 보도록 하자!

 

'apple > TCA' 카테고리의 다른 글

[TCA] Effect #6 (WebSocket)  (0) 2023.10.08
[TCA] Effect #5 (Timers)  (0) 2023.10.07
[TCA] Effect #3 (LongLiving)  (0) 2023.10.07
[TCA] Effect #2 (Cancellation)  (0) 2023.10.07
[TCA] Effect #1 (Basics)  (1) 2023.10.07