TCA๋ฅผ ๊ณต๋ถํ๋ค๊ฐ Timer ์์๋ก ๋ฌด์์ ๊ตฌํํ๋ฉด ์ข์๊น ์๊ฐํด ๋ดค๋๋ ์ผํ๋ชฐ ์ฑ์ด ๋ ์ค๋ฅด๋๋ผ๊ตฌ์! ์ค๋ ์์ ๊น์ง์ ๋จ์ ์๊ฐ์ ๊ณ์ฐํด์ ์ค๋์ด ์ง๋๋ฉด ๊ตฌ๋งคํ ์ ์๋๋ก ๋ฒํผ์ ๋นํ์ฑํ ์ํฌ๊ฑฐ์์.
ํ์ด๋จธ๋ฅผ ํ์ํ๋ ์ ๋ถ๋ถ์ด ๊ตฌ๋งค ๋ฒํผ์ ๋๋ค. ๊ตฌํํด ๋๊ณ ๋ณด๋ ํ์ด๋จธ UI์ ๊ตฌ๋งคํ๊ธฐ ๋ฒํผ์ ๋ณ๋๋ก ๋ถ๋ฆฌํ ๊ฑธ ๊ทธ๋ฌ์ด์ ๐ฅฒ
State
struct State: Equatable {
var isTimerOn = false
var leftTime = "00:00:00"
var isBuyButtonDisabled = true
}
isTimerOn์ด true๊ฐ ๋๋ฉด ํ์ด๋จธ๋ฅผ ์์ํ๊ณ false๊ฐ ๋๋ฉด ํ์ด๋จธ๋ฅผ cancel ์์ผ์ค๋๋ค. leftTime์ ๋ฒํผ ์์ญ์ ์๊ฐ ๋ถ๋ถ์ ํ์๋ ์คํธ๋ง ๊ฐ์ด์์. isBuyButtonDisabled๋ฅผ ์ด์ฉํด์ ๋ฒํผ ๋นํ์ฑํ ์ฌ๋ถ์ ๋ฒํผ์ UI๋ฅผ ์ง์ ํด ์ค๊ฑฐ์์
Action
enum Action: Equatable {
case timerToggled
case timerTicked
}
Action์ ์ฌ์ฉ์์ ํ๋์ ๋ฐ๋ผ ๋ช ์นญ์ ์ง์ด์ผ ํ๋ค๊ณ ํฉ๋๋ค. ํ๋ฉด์ ๋ํ๋ ๋ timer์ ์์ํ ๊ฑฐ๋ผ ์ด๋ป๊ฒ ์ง์ด์ผ ํ ์ง ๊ฐ์ด ์์ง ์์กํ์. ํ๋ฉด์ ๋ํ๋ ๋์ ์ฌ๋ผ์ง ๋ ๊ฐ๊ฐ ํ์ด๋จธ๋ฅผ on/off ์์ผ์ค ๊ฑฐ๋ผ toggle ์ผ์ด์ค๋ฅผ ํ๋ ๋ง๋ค์ด ์ค๋๋ค.
๊ทธ๋ฆฌ๊ณ 1์ด๋ง๋ค ๋จ์ ์๊ฐ์ ๊ณ์ฐํ๊ณ UI๋ฅผ ์ ๋ฐ์ดํธํ๊ธฐ ์ํด timerTicked๋ผ๋ Action์ ํ๋ ๋ง๋ค์ด ์ค์๋ค
Reduce
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .timerToggled:
state.isTimerOn.toggle()
if state.isTimerOn {
return .run { send in
while true {
try await Task.sleep(nanoseconds: 1_000_000_000)
await send(.timerTicked)
}
}
.cancellable(id: CancelID.timer)
} else {
return .cancel(id: CancelID.timer)
}
case .timerTicked:
let now = Date()
let endOfDay = Calendar.current.date(bySettingHour: 23, minute: 59, second: 59, of: now)!
let timeInterval = endOfDay.timeIntervalSince(now)
if timeInterval > 0 {
let hours = Int(timeInterval) / 3600
let minutes = Int(timeInterval) / 60 % 60
let seconds = Int(timeInterval) % 60
state.leftTime = String(format: "%02i:%02i:%02i", hours, minutes, seconds)
state.isBuyButtonDisabled = false
} else {
state.leftTime = "00:00:00"
state.isBuyButtonDisabled = true
}
return .none
}
}
timerToggled action์ด ๋ฐ์ํ๋ฉด isTimerOn์ด true ์ผ๋ 1์ด๋ง๋ค timerTicked ์ก์ ์ send ํด์ค์. ๊ทธ๋ฌ๋ฉด timerTicked์์๋ ์ค๋๋ก ๋ถํฐ ๋จ์ ์๊ฐ์ ์นด์ดํธํด์ leftTime๊ณผ isBuyButtonDisabled State ๊ฐ์ ์ ๋ฐ์ดํธ ํด์ค๋๋ค.
UI
Button {
// TODO: add action
} label: {
VStack {
Text(viewStore.isBuyButtonDisabled ? "๋ค์ ๊ธฐํ์...!" : "์ค๋์ด ์ง๋๋ฉด ๊ตฌ๋งคํ ์ ์์ด์!")
.foregroundStyle(.white)
Text(viewStore.leftTime)
.font(.title)
.foregroundStyle(.white)
}
.padding(.vertical, 20)
.frame(maxWidth: .infinity)
.background(viewStore.isBuyButtonDisabled ? .gray : .black)
}
.disabled(viewStore.isBuyButtonDisabled)
isBuyButtonDisabled์ leftTime state๋ฅผ store ๋ก๋ถํฐ ์ ๋ฌ ๋ฐ์ UI ๊ตฌ์ฑํ๋๋ฐ ์ฌ์ฉํด ์ฃผ๋ฉด ๋ฉ๋๋ค
'iOS ๐ > Architecture' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[SwiftUI/TCA] Binding (0) | 2023.12.07 |
---|---|
[SwiftUI/TCA] Scope (1) | 2023.11.30 |
[SwiftUI/TCA] Store and ViewStore (0) | 2023.11.29 |
[SwiftUI/TCA] Effect ๊ตฌํ๊ณผ ํ์ฉ (0) | 2023.11.29 |
[SwiftUI/TCA] TCA ๊ธฐ๋ณธ ๊ฐ๋ ์ ๋ํด ์์๋ณด๊ธฐ (0) | 2023.11.21 |