์ผ | ์ | ํ | ์ | ๋ชฉ | ๊ธ | ํ |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 |
30 |
Tags
- SnapKit
- MVVM
- UIKit
- Kuring
- ๋ฐฑ์ค
- realm
- ํ๋ก๊ทธ๋๋จธ์ค
- reactorkit
- CollectionView
- node.js
- swift
- ios
- ํจ์คํธ์บ ํผ์ค
- XCTest
- arkit
- Xcode
- TCA
- Flutter
- designpattern
- Lv2
- BFS
- combine
- BOJ
- visionOS
- SwiftUI
- RxSwift
- Swfit
- tableView
- rxcocoa
- raywenderlich
Archives
- Today
- Total
lgvv98
[TCA] Effect #1 (Basics) ๋ณธ๋ฌธ
[TCA] Effect #1 (Basics)
๋ชฉ์ฐจ
- Effect๋?
- Effect Basic ์์ ์ดํด๋ณด๊ธฐ
- Effect๋?
TCA๋ก ๋ง๋ค์ด์ง ๊ธฐ๋ฅ์ Side Effect์ ๋์ ํ ์ ์์.
Side Effect๋ ์ธ๋ถ์์ ์ํ๋์ด์ผ ํ๋ ์์ ๋ค๋ก API ์์ฒญ, HTTP๋ฅผ ํตํด ์ธ๋ถ ์๋น์ค๋ฅผ ์ฌ์ฉํ๋ ๋ฑ ๋ถํ์คํ๋ฉฐ ๋ณต์กํ๊ธฐ๋ ํจ.
- ์์ด ์ฉ์ด ์ ๋ฆฌ
NB: nota bene๋ผ๋ ๋ผํด์ด๋ก ์ฃผ์, ์ ์๋ผ๋ ์๋ฏธ
- Effect Basic ์์ ์ดํด๋ณด๊ธฐ
import ComposableArchitecture
import SwiftUI
// MARK: - Feature domain
struct EffectsBasics: Reducer {
struct State: Equatable {
var count = 0
var isNumberFactRequestInFlight = false
var numberFact: String?
}
enum Action: Equatable {
case decrementButtonTapped
case decrementDelayResponse
case incrementButtonTapped
case numberFactButtonTapped
case numberFactResponse(TaskResult<String>)
}
@Dependency(\.continuousClock) var clock
@Dependency(\.factClient) var factClient
private enum CancelID { case delay }
func reduce(into state: inout State, action: Action) -> Effect<Action> {
switch action {
case .decrementButtonTapped:
state.count -= 1
state.numberFact = nil
// ๋ง์ฝ ์๊ฐ ์์๋ผ๋ฉด 1์ด ํ์ ์๋ฅผ ๋ค์ 1 ์ฌ์ฆ๊ฐ ์์ผ์ฃผ๋ ๊ตฌํ
return state.count >= 0
? .none
: .run { send in
try await self.clock.sleep(for: .seconds(1)) // 1์ด ๊ธฐ๋ค๋ ธ๋ค๊ฐ
await send(.decrementDelayResponse) // 1์ด ๋ค ๋๋ ์ด
}
.cancellable(id: CancelID.delay) // ์ถ์ธก?: ํด๋น ์ด๋ฒคํธ๋ฅผ ๊ตฌ๋
ํ๊ณ , ํน์ ์์ ์ ์ทจ์ ์ํค๊ธฐ ์ํด ์ด๋ฒคํธ๋ฅผ ์ทจ์์ํค๊ธฐ ์ํ์ฌ ๋ฑ๋ก
case .decrementDelayResponse:
if state.count < 0 {
state.count += 1 // ๊ฐ์ ์ด๋ฒคํธ๊ฐ ๋์ฐฉํ๋ฉด ์์์ผ ๋๋ง ์ฌ๋ฆผ
}
return .none
case .incrementButtonTapped:
state.count += 1
state.numberFact = nil
return state.count >= 0
? .cancel(id: CancelID.delay) // cancel์ ํตํ ๋๋ ์ด ๋ฐ์ธ๋ฉ ์ ๊ฑฐ
: .none
case .numberFactButtonTapped:
state.isNumberFactRequestInFlight = true
state.numberFact = nil
// API๋ฅผ ํตํด ๋ฐ์ ์จ ๊ฐ์ ๋ฐํํ๋ค
// `numberFactResponse` action์ผ๋ก value ๋ฐํ
return .run { [count = state.count] send in
// TaskResult๋ enum์ผ๋ก Result์ ๋์ผํ๊ฒ ์ฌ์ฉ ๊ฐ๋ฅ
await send(.numberFactResponse(TaskResult { try await self.factClient.fetch(count) }))
}
case let .numberFactResponse(.success(response)):
state.isNumberFactRequestInFlight = false
state.numberFact = response
return .none
case .numberFactResponse(.failure):
// ์ฃผ์: ๊ฒฝ๊ณ ์๋ฟ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ์๋ฌ๋ฅผ ํธ๋ค๋ง์ ํ ์ ์๋ ๋ถ๋ถ
state.isNumberFactRequestInFlight = false
return .none
}
}
}
// MARK: - Feature view
struct EffectsBasicsView: View {
let store: StoreOf<EffectsBasics>
@Environment(\.openURL) var openURL // ์ธ๋ถ์์ ์ ์๋์ด ์์
var body: some View {
WithViewStore(self.store, observe: { $0 }) { viewStore in
Form {
Section {
AboutView(readMe: readMe)
}
Section {
HStack {
Button {
viewStore.send(.decrementButtonTapped)
} label: {
Image(systemName: "minus")
}
Text("\(viewStore.count)")
.monospacedDigit()
Button {
viewStore.send(.incrementButtonTapped)
} label: {
Image(systemName: "plus")
}
}
.frame(maxWidth: .infinity)
Button("Number fact") { viewStore.send(.numberFactButtonTapped) }
.frame(maxWidth: .infinity)
if viewStore.isNumberFactRequestInFlight {
ProgressView()
.frame(maxWidth: .infinity)
// Swift UI์ ์๋ก์ด ID๊ฐ ์ฃผ์ด์ง์ง ์์ผ๋ฉด ํ๋ก๊ทธ๋ ์ค ๋ทฐ๊ฐ ๋๋ฒ์งธ์๋ ๋ํ๋์ง ์๋ ๋ฒ๊ทธ๊ฐ ์๋ ๊ฒ ๊ฐ๋ค๊ณ ํจ.
// ํ๋ฒ์ ๋ํ๋๊ณ ๋๋ฒ์ ๋ํ๋์ง ์๋๋ค๋ ๋ง
.id(UUID())
}
if let numberFact = viewStore.numberFact {
Text(numberFact)
}
}
Section {
Button("Number facts provided by numbersapi.com") {
self.openURL(URL(string: "http://numbersapi.com")!)
}
.foregroundStyle(.secondary)
.frame(maxWidth: .infinity)
}
}
.buttonStyle(.borderless)
}
.navigationTitle("Effects")
}
}
// MARK: - SwiftUI previews
struct EffectsBasicsView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
EffectsBasicsView(
store: Store(initialState: EffectsBasics.State()) {
EffectsBasics()
}
)
}
}
}
TaskResult์ enum Cancel { } ์ ์ฌ์ฉํ๋ ๋ถ๋ถ์ ์๊ฒ ๋์์.
ํนํ enum Cancel ๋ถ๋ถ์ ์กฐ๊ธ ๋ ์ธ๋ฐํ๊ฒ ์ปจํธ๋กค ํ๋๊ฒ ํ์ํ ๋ฏ ์ถ๋ค
'apple > ๐ฆฅ TCA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[TCA] Effect #3 (LongLiving) (0) | 2023.10.07 |
---|---|
[TCA] Effect #2 (Cancellation) (0) | 2023.10.07 |
[TCA] SharedState (1) | 2023.09.27 |
[TCA] OptionalState (IfLetCase) (0) | 2023.09.27 |
[TCA] FocusState (0) | 2023.09.27 |
Comments