iOS ๐ŸŽ/Architecture

[SwiftUI/TCA] Timer ๋งŒ๋“ค๊ธฐ

fram 2023. 11. 24. 00:32

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 ๊ตฌ์„ฑํ•˜๋Š”๋ฐ ์‚ฌ์šฉํ•ด ์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค

 

 

๋Œ“๊ธ€์ˆ˜0