From 64e1f77cae5b54ec05e21e97756a6766f3dcaa1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 11 Nov 2025 17:56:44 +0900 Subject: [PATCH 1/4] =?UTF-8?q?[Feature]=20Timer=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20Live=20Ac?= =?UTF-8?q?tivity=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Timer 모듈 폴더 구조 정리 (Container, Intent, Model, Widget 분리) - TimerActivityAttributes 추가 (Live Activity 데이터 모델) - TimerLiveActivity UI 구현 - Lock Screen View (타이머 상태 표시) - Dynamic Island (expanded, compact, minimal) - 남은 시간, 전체 시간, 실행 상태 표시 - WakeyAlarmWidgetBundle에 TimerLiveActivity 등록 - Widget 모듈에 Timer 의존성 추가 --- .../{ => Container}/TimerContainer.swift | 0 .../Sources/{ => Intent}/TimerIntent.swift | 0 .../Sources/{ => Model}/TimerModel.swift | 0 .../Sources/{ => Model}/TimerSideEffect.swift | 0 .../Sources/{ => Model}/TimerState.swift | 0 .../Widget/TimerActivityAttributes.swift | 68 ++++++++ Projects/Presentation/Widget/Project.swift | 3 +- .../Widget/Sources/TimerLiveActivity.swift | 165 ++++++++++++++++++ .../Widget/Sources/WakeyAlarmWidget.swift | 8 +- 9 files changed, 237 insertions(+), 7 deletions(-) rename Projects/Presentation/Timer/Sources/{ => Container}/TimerContainer.swift (100%) rename Projects/Presentation/Timer/Sources/{ => Intent}/TimerIntent.swift (100%) rename Projects/Presentation/Timer/Sources/{ => Model}/TimerModel.swift (100%) rename Projects/Presentation/Timer/Sources/{ => Model}/TimerSideEffect.swift (100%) rename Projects/Presentation/Timer/Sources/{ => Model}/TimerState.swift (100%) create mode 100644 Projects/Presentation/Timer/Sources/Widget/TimerActivityAttributes.swift create mode 100644 Projects/Presentation/Widget/Sources/TimerLiveActivity.swift diff --git a/Projects/Presentation/Timer/Sources/TimerContainer.swift b/Projects/Presentation/Timer/Sources/Container/TimerContainer.swift similarity index 100% rename from Projects/Presentation/Timer/Sources/TimerContainer.swift rename to Projects/Presentation/Timer/Sources/Container/TimerContainer.swift diff --git a/Projects/Presentation/Timer/Sources/TimerIntent.swift b/Projects/Presentation/Timer/Sources/Intent/TimerIntent.swift similarity index 100% rename from Projects/Presentation/Timer/Sources/TimerIntent.swift rename to Projects/Presentation/Timer/Sources/Intent/TimerIntent.swift diff --git a/Projects/Presentation/Timer/Sources/TimerModel.swift b/Projects/Presentation/Timer/Sources/Model/TimerModel.swift similarity index 100% rename from Projects/Presentation/Timer/Sources/TimerModel.swift rename to Projects/Presentation/Timer/Sources/Model/TimerModel.swift diff --git a/Projects/Presentation/Timer/Sources/TimerSideEffect.swift b/Projects/Presentation/Timer/Sources/Model/TimerSideEffect.swift similarity index 100% rename from Projects/Presentation/Timer/Sources/TimerSideEffect.swift rename to Projects/Presentation/Timer/Sources/Model/TimerSideEffect.swift diff --git a/Projects/Presentation/Timer/Sources/TimerState.swift b/Projects/Presentation/Timer/Sources/Model/TimerState.swift similarity index 100% rename from Projects/Presentation/Timer/Sources/TimerState.swift rename to Projects/Presentation/Timer/Sources/Model/TimerState.swift diff --git a/Projects/Presentation/Timer/Sources/Widget/TimerActivityAttributes.swift b/Projects/Presentation/Timer/Sources/Widget/TimerActivityAttributes.swift new file mode 100644 index 0000000..d1c4bbb --- /dev/null +++ b/Projects/Presentation/Timer/Sources/Widget/TimerActivityAttributes.swift @@ -0,0 +1,68 @@ +// +// TimerActivityAttributes.swift +// Timer +// +// Created by 홍석현 on 11/11/25. +// + +import Foundation +import ActivityKit + +public struct TimerActivityAttributes: ActivityAttributes { + + public struct ContentState: Codable, Hashable { + public var totalTime: TimeInterval + public var remainingTime: TimeInterval + public var endDate: Date? + public var isRunning: Bool + public var isPaused: Bool + + public init( + totalTime: TimeInterval, + remainingTime: TimeInterval, + endDate: Date? = nil, + isRunning: Bool, + isPaused: Bool + ) { + self.totalTime = totalTime + self.remainingTime = remainingTime + self.endDate = endDate + self.isRunning = isRunning + self.isPaused = isPaused + } + } + + public init() { + + } + +} + +// MARK: - Preview Support +extension TimerActivityAttributes { + public static var preview: TimerActivityAttributes { + TimerActivityAttributes() + } +} + +extension TimerActivityAttributes.ContentState { + public static var sample: TimerActivityAttributes.ContentState { + TimerActivityAttributes.ContentState( + totalTime: 300, + remainingTime: 185, + endDate: Date().addingTimeInterval(185), + isRunning: true, + isPaused: false + ) + } + + public static var almostDone: TimerActivityAttributes.ContentState { + TimerActivityAttributes.ContentState( + totalTime: 300, + remainingTime: 60, + endDate: Date().addingTimeInterval(60), + isRunning: true, + isPaused: false + ) + } +} diff --git a/Projects/Presentation/Widget/Project.swift b/Projects/Presentation/Widget/Project.swift index cc1a67d..98e3c57 100644 --- a/Projects/Presentation/Widget/Project.swift +++ b/Projects/Presentation/Widget/Project.swift @@ -17,7 +17,8 @@ let project = Project( sources: ["Sources/**"], resources: ["Resources/**"], dependencies: [ - .Presentation(implements: .StopWatch) + .Presentation(implements: .StopWatch), + .Presentation(implements: .Timer) // WidgetKit과 SwiftUI는 시스템 프레임워크로 자동 링크됨 ] ) diff --git a/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift b/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift new file mode 100644 index 0000000..ecc8602 --- /dev/null +++ b/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift @@ -0,0 +1,165 @@ +// +// TimerLiveActivity.swift +// Timer +// +// Created by 홍석현 on 11/11/25. +// + +import ActivityKit +import WidgetKit +import SwiftUI +import Timer +import DesignSystem + +public struct TimerLiveActivity: Widget { + + public init() {} + + public var body: some WidgetConfiguration { + ActivityConfiguration(for: TimerActivityAttributes.self) { (context: ActivityViewContext) in + lockScreenView(context: context) + } dynamicIsland: { (context: ActivityViewContext) in + DynamicIsland { + DynamicIslandExpandedRegion(.leading) { + HStack { + Image(systemName: "timer") + .foregroundColor(.blue) + + Text("타이머") + .font(.pretendardFont(family: .medium, size: 14)) + .foregroundStyle(.whiteSmoke) + } + .padding(.leading, 10) + } + + DynamicIslandExpandedRegion(.trailing) { + VStack(alignment: .trailing) { + Text(formatTime(context.state.remainingTime)) + .font(.pretendardFont(family: .bold, size: 18)) + .foregroundStyle(.whiteSmoke) + .monospacedDigit() + + Text("남은 시간") + .font(.pretendardFont(family: .medium, size: 12)) + .foregroundColor(.secondary) + } + .padding(.trailing, 10) + } + + DynamicIslandExpandedRegion(.bottom) { + HStack { + VStack(alignment: .leading) { + Text("전체 시간") + .font(.pretendardFont(family: .medium, size: 12)) + .foregroundColor(.secondary) + + Text(formatTime(context.state.totalTime)) + .font(.pretendardFont(family: .medium, size: 14)) + .monospacedDigit() + } + + Spacer() + + HStack(spacing: 4) { + Circle() + .fill(context.state.isRunning && !context.state.isPaused ? .green : .gray) + .frame(width: 8, height: 8) + + Text(context.state.isRunning && !context.state.isPaused ? "실행 중" : "정지됨") + .font(.pretendardFont(family: .medium, size: 12)) + .foregroundColor(.secondary) + } + } + .padding(.horizontal, 10) + } + } compactLeading: { + Image(systemName: "timer") + .foregroundColor(.blue) + .padding(.leading, 10) + } compactTrailing: { + Text(formatTime(context.state.remainingTime)) + .font(.pretendardFont(family: .medium, size: 12)) + .monospacedDigit() + .padding(.trailing, 10) + } minimal: { + Image(systemName: context.state.isRunning && !context.state.isPaused ? "play.fill" : "pause.fill") + .foregroundColor(context.state.isRunning && !context.state.isPaused ? .green : .orange) + } + } + } + + @ViewBuilder + private func lockScreenView( + context: ActivityViewContext + ) -> some View { + VStack(spacing: 12) { + HStack { + Image(systemName: "timer") + .foregroundColor(.blue) + + Text("타이머") + .font(.headline) + .fontWeight(.medium) + + Spacer() + + Text(context.state.isRunning && !context.state.isPaused ? "실행 중" : "정지됨") + .font(.caption) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + Capsule() + .fill(context.state.isRunning && !context.state.isPaused ? .green.opacity(0.2) : .gray.opacity(0.2)) + ) + .foregroundColor(context.state.isRunning && !context.state.isPaused ? .green : .secondary) + } + + VStack(spacing: 4) { + Text(formatTime(context.state.remainingTime)) + .font(.system(size: 32, weight: .bold, design: .monospaced)) + .foregroundColor(.primary) + + Text("남은 시간") + .font(.caption) + .foregroundColor(.secondary) + } + + Divider() + + HStack { + Text("전체 시간") + .font(.caption) + .foregroundColor(.secondary) + Spacer() + Text(formatTime(context.state.totalTime)) + .font(.caption) + .fontWeight(.medium) + .monospacedDigit() + .foregroundColor(.primary) + } + } + .padding() + .background( + RoundedRectangle(cornerRadius: 16) + .fill(.ultraThinMaterial) + ) + } + + private func formatTime(_ timeInterval: TimeInterval) -> String { + let minutes = Int(timeInterval) / 60 + let seconds = Int(timeInterval) % 60 + return String(format: "%02d:%02d", minutes, seconds) + } +} + +@available(iOS 17.0, *) +#Preview( + "타이머 잠금 화면", + as: .content, + using: TimerActivityAttributes.preview +) { + TimerLiveActivity() +} contentStates: { + TimerActivityAttributes.ContentState.sample + TimerActivityAttributes.ContentState.almostDone +} diff --git a/Projects/Presentation/Widget/Sources/WakeyAlarmWidget.swift b/Projects/Presentation/Widget/Sources/WakeyAlarmWidget.swift index 392e7d8..d588480 100644 --- a/Projects/Presentation/Widget/Sources/WakeyAlarmWidget.swift +++ b/Projects/Presentation/Widget/Sources/WakeyAlarmWidget.swift @@ -5,11 +5,7 @@ import SwiftUI @main struct WakeyAlarmWidgetBundle: WidgetBundle { var body: some Widget { - // iOS 16.1+ Live Activity 지원 - #if canImport(ActivityKit) - if #available(iOS 16.1, *) { - StopWatchLiveActivity() - } - #endif + StopWatchLiveActivity() + TimerLiveActivity() } } From bdddb66d43d8b221e4ae7bdbc4f1fc8174eba44d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 11 Nov 2025 18:43:57 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat.=20timer=20activity=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/Container/TimerContainer.swift | 106 ++++++- .../Timer/Sources/Intent/TimerIntent.swift | 1 + .../Timer/Sources/Model/TimerModel.swift | 9 + .../Timer/Sources/Model/TimerState.swift | 5 +- .../Widget/Sources/TimerLiveActivity.swift | 296 ++++++++++++------ 5 files changed, 308 insertions(+), 109 deletions(-) diff --git a/Projects/Presentation/Timer/Sources/Container/TimerContainer.swift b/Projects/Presentation/Timer/Sources/Container/TimerContainer.swift index 19ed87f..b023ac7 100644 --- a/Projects/Presentation/Timer/Sources/Container/TimerContainer.swift +++ b/Projects/Presentation/Timer/Sources/Container/TimerContainer.swift @@ -8,6 +8,7 @@ import Foundation import Combine +import ActivityKit // MARK: - Timer Container (ViewModel) public final class TimerContainer: ObservableObject { @@ -18,6 +19,7 @@ public final class TimerContainer: ObservableObject { private let model: TimerModelProtocol private var cancellables = Set() private var timerCancellable: AnyCancellable? + private var currentActivity: Activity? // MARK: - Initialization public init( @@ -43,12 +45,15 @@ public final class TimerContainer: ObservableObject { switch sideEffect { case .startTimerTicking: startTimer() + startLiveActivity() case .stopTimerTicking: stopTimer() + updateLiveActivity() case .playAlarm: playAlarm() + endLiveActivity() case .showCompletionAlert: showCompletionAlert() @@ -56,8 +61,11 @@ public final class TimerContainer: ObservableObject { } // MARK: - Timer Management + private var tickCount: Int = 0 + private func startTimer() { stopTimer() // 기존 타이머 정리 + tickCount = 0 // 애니메이션과 동기화를 위해 0.3초 지연 Task { @MainActor in @@ -68,10 +76,22 @@ public final class TimerContainer: ObservableObject { .sink { [weak self] _ in guard let self = self else { return } - if self.state.remainingTime > 0 { - self.send(.timerTick) - } else { - self.send(.timerCompleted) + // endDate 기반으로 남은 시간 계산 (백그라운드에서도 정확함) + if let endDate = self.state.endDate { + let remaining = endDate.timeIntervalSinceNow + + if remaining > 0 { + self.send(.updateRemainingTime(remaining)) + + // 1초마다 Live Activity 업데이트 (10틱 = 1초) + self.tickCount += 1 + if self.tickCount >= 10 { + self.updateLiveActivity() + self.tickCount = 0 + } + } else { + self.send(.timerCompleted) + } } } } @@ -93,8 +113,86 @@ public final class TimerContainer: ObservableObject { print("✅ 타이머 완료 알림") } + // MARK: - Live Activity Management + private func startLiveActivity() { + let authInfo = ActivityAuthorizationInfo() + guard authInfo.areActivitiesEnabled else { + print("❌ Live Activities가 비활성화되어 있습니다.") + return + } + + if currentActivity != nil { + updateLiveActivity() + return + } + + Task { + let contentState = TimerActivityAttributes.ContentState( + totalTime: state.totalTime, + remainingTime: state.remainingTime, + endDate: state.endDate, + isRunning: state.isRunning, + isPaused: state.isPaused + ) + + do { + let activity = try Activity.request( + attributes: TimerActivityAttributes(), + content: .init(state: contentState, staleDate: nil) + ) + currentActivity = activity + print("✅ Timer Live Activity 시작: \(activity.id)") + } catch { + print("❌ Timer Live Activity 시작 실패: \(error)") + } + } + } + + private func updateLiveActivity() { + guard let activity = currentActivity else { return } + + Task { + let contentState = TimerActivityAttributes.ContentState( + totalTime: state.totalTime, + remainingTime: state.remainingTime, + endDate: state.endDate, + isRunning: state.isRunning, + isPaused: state.isPaused + ) + + let staleDate = Date().addingTimeInterval(2) + await activity.update(.init(state: contentState, staleDate: staleDate)) + } + } + + private func endLiveActivity() { + guard let activity = currentActivity else { return } + + Task { + let finalState = TimerActivityAttributes.ContentState( + totalTime: state.totalTime, + remainingTime: 0, + endDate: nil, + isRunning: false, + isPaused: false + ) + + await activity.end( + .init(state: finalState, staleDate: nil), + dismissalPolicy: .default + ) + currentActivity = nil + print("✅ Timer Live Activity 종료") + } + } + // MARK: - Cleanup deinit { stopTimer() + Task { @MainActor [weak self] in + if let activity = self?.currentActivity { + await activity.end(nil, dismissalPolicy: .immediate) + } + } } } diff --git a/Projects/Presentation/Timer/Sources/Intent/TimerIntent.swift b/Projects/Presentation/Timer/Sources/Intent/TimerIntent.swift index d6a2812..efd29f2 100644 --- a/Projects/Presentation/Timer/Sources/Intent/TimerIntent.swift +++ b/Projects/Presentation/Timer/Sources/Intent/TimerIntent.swift @@ -18,5 +18,6 @@ public enum TimerIntent { case resumeTimer case cancelTimer case timerTick + case updateRemainingTime(TimeInterval) case timerCompleted } diff --git a/Projects/Presentation/Timer/Sources/Model/TimerModel.swift b/Projects/Presentation/Timer/Sources/Model/TimerModel.swift index c94f82d..389197c 100644 --- a/Projects/Presentation/Timer/Sources/Model/TimerModel.swift +++ b/Projects/Presentation/Timer/Sources/Model/TimerModel.swift @@ -40,20 +40,24 @@ public final class TimerModel: TimerModelProtocol { newState.remainingTime = TimeInterval(totalSeconds) newState.progress = 1.0 newState.timerStatus = .running + newState.endDate = Date().addingTimeInterval(TimeInterval(totalSeconds)) sideEffect = .startTimerTicking case .pauseTimer: newState.timerStatus = .paused + newState.endDate = nil sideEffect = .stopTimerTicking case .resumeTimer: newState.timerStatus = .running + newState.endDate = Date().addingTimeInterval(state.remainingTime) sideEffect = .startTimerTicking case .cancelTimer: newState.timerStatus = .idle newState.remainingTime = 0 newState.progress = 1.0 + newState.endDate = nil sideEffect = .stopTimerTicking case .timerTick: @@ -62,10 +66,15 @@ public final class TimerModel: TimerModelProtocol { newState.progress = state.totalTime > 0 ? newState.remainingTime / state.totalTime : 0 } + case .updateRemainingTime(let remaining): + newState.remainingTime = max(0, remaining) + newState.progress = state.totalTime > 0 ? newState.remainingTime / state.totalTime : 0 + case .timerCompleted: newState.timerStatus = .idle newState.remainingTime = 0 newState.progress = 1.0 + newState.endDate = nil sideEffect = .playAlarm } diff --git a/Projects/Presentation/Timer/Sources/Model/TimerState.swift b/Projects/Presentation/Timer/Sources/Model/TimerState.swift index 8de9948..d277ada 100644 --- a/Projects/Presentation/Timer/Sources/Model/TimerState.swift +++ b/Projects/Presentation/Timer/Sources/Model/TimerState.swift @@ -20,6 +20,7 @@ public struct TimerState: Equatable { public var remainingTime: TimeInterval public var totalTime: TimeInterval public var progress: Double + public var endDate: Date? public init( selectedHours: Int = 0, @@ -28,7 +29,8 @@ public struct TimerState: Equatable { timerStatus: TimerStatus = .idle, remainingTime: TimeInterval = 0, totalTime: TimeInterval = 0, - progress: Double = 1.0 + progress: Double = 1.0, + endDate: Date? = nil ) { self.selectedHours = selectedHours self.selectedMinutes = selectedMinutes @@ -37,6 +39,7 @@ public struct TimerState: Equatable { self.remainingTime = remainingTime self.totalTime = totalTime self.progress = progress + self.endDate = endDate } } diff --git a/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift b/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift index ecc8602..77bbc64 100644 --- a/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift +++ b/Projects/Presentation/Widget/Sources/TimerLiveActivity.swift @@ -16,142 +16,230 @@ public struct TimerLiveActivity: Widget { public init() {} public var body: some WidgetConfiguration { - ActivityConfiguration(for: TimerActivityAttributes.self) { (context: ActivityViewContext) in - lockScreenView(context: context) - } dynamicIsland: { (context: ActivityViewContext) in + ActivityConfiguration(for: TimerActivityAttributes.self) { context in + TimerLockScreenView(context: context) + } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.leading) { - HStack { - Image(systemName: "timer") - .foregroundColor(.blue) - - Text("타이머") - .font(.pretendardFont(family: .medium, size: 14)) - .foregroundStyle(.whiteSmoke) - } - .padding(.leading, 10) + TimerExpandedLeadingView() } DynamicIslandExpandedRegion(.trailing) { - VStack(alignment: .trailing) { - Text(formatTime(context.state.remainingTime)) - .font(.pretendardFont(family: .bold, size: 18)) - .foregroundStyle(.whiteSmoke) - .monospacedDigit() - - Text("남은 시간") - .font(.pretendardFont(family: .medium, size: 12)) - .foregroundColor(.secondary) - } - .padding(.trailing, 10) + TimerExpandedTrailingView(context: context) } DynamicIslandExpandedRegion(.bottom) { - HStack { - VStack(alignment: .leading) { - Text("전체 시간") - .font(.pretendardFont(family: .medium, size: 12)) - .foregroundColor(.secondary) - - Text(formatTime(context.state.totalTime)) - .font(.pretendardFont(family: .medium, size: 14)) - .monospacedDigit() - } - - Spacer() - - HStack(spacing: 4) { - Circle() - .fill(context.state.isRunning && !context.state.isPaused ? .green : .gray) - .frame(width: 8, height: 8) - - Text(context.state.isRunning && !context.state.isPaused ? "실행 중" : "정지됨") - .font(.pretendardFont(family: .medium, size: 12)) - .foregroundColor(.secondary) - } - } - .padding(.horizontal, 10) + TimerExpandedBottomView(context: context) } } compactLeading: { - Image(systemName: "timer") - .foregroundColor(.blue) - .padding(.leading, 10) + TimerCompactLeadingView() } compactTrailing: { - Text(formatTime(context.state.remainingTime)) - .font(.pretendardFont(family: .medium, size: 12)) - .monospacedDigit() - .padding(.trailing, 10) + TimerCompactTrailingView(remainingTime: context.state.remainingTime) } minimal: { - Image(systemName: context.state.isRunning && !context.state.isPaused ? "play.fill" : "pause.fill") - .foregroundColor(context.state.isRunning && !context.state.isPaused ? .green : .orange) + TimerMinimalView(isActive: context.state.isRunning && !context.state.isPaused) } } } +} + +// MARK: - Lock Screen Components + +private struct TimerLockScreenView: View { + let context: ActivityViewContext - @ViewBuilder - private func lockScreenView( - context: ActivityViewContext - ) -> some View { + var body: some View { VStack(spacing: 12) { - HStack { - Image(systemName: "timer") - .foregroundColor(.blue) - - Text("타이머") - .font(.headline) - .fontWeight(.medium) - - Spacer() - - Text(context.state.isRunning && !context.state.isPaused ? "실행 중" : "정지됨") - .font(.caption) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background( - Capsule() - .fill(context.state.isRunning && !context.state.isPaused ? .green.opacity(0.2) : .gray.opacity(0.2)) - ) - .foregroundColor(context.state.isRunning && !context.state.isPaused ? .green : .secondary) - } + TimerHeaderView(isActive: context.state.isRunning && !context.state.isPaused) + TimerMainTimeView(remainingTime: context.state.remainingTime) + Divider() + TimerTotalTimeRow(totalTime: context.state.totalTime) + } + .padding() + .background( + RoundedRectangle(cornerRadius: 16) + .fill(.ultraThinMaterial) + ) + } +} - VStack(spacing: 4) { - Text(formatTime(context.state.remainingTime)) - .font(.system(size: 32, weight: .bold, design: .monospaced)) - .foregroundColor(.primary) +private struct TimerHeaderView: View { + let isActive: Bool - Text("남은 시간") - .font(.caption) - .foregroundColor(.secondary) - } + var body: some View { + HStack { + Image(systemName: "timer") + .foregroundColor(.blue) - Divider() + Text("타이머") + .font(.headline) + .fontWeight(.medium) + + Spacer() + + StatusBadge(isActive: isActive) + } + } +} + +private struct StatusBadge: View { + let isActive: Bool + + var body: some View { + Text(isActive ? "실행 중" : "정지됨") + .font(.caption) + .padding(.horizontal, 8) + .padding(.vertical, 4) + .background( + Capsule() + .fill(isActive ? .green.opacity(0.2) : .gray.opacity(0.2)) + ) + .foregroundColor(isActive ? .green : .secondary) + } +} + +private struct TimerMainTimeView: View { + let remainingTime: TimeInterval + + var body: some View { + VStack(spacing: 4) { + Text(formatTime(remainingTime)) + .font(.system(size: 32, weight: .bold, design: .monospaced)) + .foregroundColor(.primary) + + Text("남은 시간") + .font(.caption) + .foregroundColor(.secondary) + } + } +} + +private struct TimerTotalTimeRow: View { + let totalTime: TimeInterval + + var body: some View { + HStack { + Text("전체 시간") + .font(.caption) + .foregroundColor(.secondary) + Spacer() + Text(formatTime(totalTime)) + .font(.caption) + .fontWeight(.medium) + .monospacedDigit() + .foregroundColor(.primary) + } + } +} + +// MARK: - Dynamic Island Components + +private struct TimerExpandedLeadingView: View { + var body: some View { + HStack { + Image(systemName: "timer") + .foregroundColor(.blue) - HStack { + Text("타이머") + .font(.pretendardFont(family: .medium, size: 14)) + .foregroundStyle(.whiteSmoke) + } + .padding(.leading, 10) + } +} + +private struct TimerExpandedTrailingView: View { + let context: ActivityViewContext + + var body: some View { + VStack(alignment: .trailing) { + Text(formatTime(context.state.remainingTime)) + .font(.pretendardFont(family: .bold, size: 18)) + .foregroundStyle(.whiteSmoke) + .monospacedDigit() + + Text("남은 시간") + .font(.pretendardFont(family: .medium, size: 12)) + .foregroundColor(.secondary) + } + .padding(.trailing, 10) + } +} + +private struct TimerExpandedBottomView: View { + let context: ActivityViewContext + + var body: some View { + HStack { + VStack(alignment: .leading) { Text("전체 시간") - .font(.caption) + .font(.pretendardFont(family: .medium, size: 12)) .foregroundColor(.secondary) - Spacer() + Text(formatTime(context.state.totalTime)) - .font(.caption) - .fontWeight(.medium) + .font(.pretendardFont(family: .medium, size: 14)) .monospacedDigit() - .foregroundColor(.primary) } + + Spacer() + + StatusIndicator(isActive: context.state.isRunning && !context.state.isPaused) + } + .padding(.horizontal, 10) + } +} + +private struct StatusIndicator: View { + let isActive: Bool + + var body: some View { + HStack(spacing: 4) { + Circle() + .fill(isActive ? .green : .gray) + .frame(width: 8, height: 8) + + Text(isActive ? "실행 중" : "정지됨") + .font(.pretendardFont(family: .medium, size: 12)) + .foregroundColor(.secondary) } - .padding() - .background( - RoundedRectangle(cornerRadius: 16) - .fill(.ultraThinMaterial) - ) } +} - private func formatTime(_ timeInterval: TimeInterval) -> String { - let minutes = Int(timeInterval) / 60 - let seconds = Int(timeInterval) % 60 - return String(format: "%02d:%02d", minutes, seconds) +private struct TimerCompactLeadingView: View { + var body: some View { + Image(systemName: "timer") + .foregroundColor(.blue) + .padding(.leading, 10) } } +private struct TimerCompactTrailingView: View { + let remainingTime: TimeInterval + + var body: some View { + Text(formatTime(remainingTime)) + .font(.pretendardFont(family: .medium, size: 12)) + .monospacedDigit() + .padding(.trailing, 10) + } +} + +private struct TimerMinimalView: View { + let isActive: Bool + + var body: some View { + Image(systemName: isActive ? "play.fill" : "pause.fill") + .foregroundColor(isActive ? .green : .orange) + } +} + +// MARK: - Helper Functions + +private func formatTime(_ timeInterval: TimeInterval) -> String { + let minutes = Int(timeInterval) / 60 + let seconds = Int(timeInterval) % 60 + return String(format: "%02d:%02d", minutes, seconds) +} + @available(iOS 17.0, *) #Preview( "타이머 잠금 화면", From e7e1b6c23b9a70ab3cbf218597d1fa7ed6186e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 11 Nov 2025 19:19:14 +0900 Subject: [PATCH 3/4] =?UTF-8?q?chore:=20=EA=B7=B8=EB=9E=98=ED=94=84=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- graph.png | Bin 31839 -> 64232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/graph.png b/graph.png index f1542e03a63f8d7876044af298ab981fbf9407a9..f939d56a78b4b40c4ebdfb5c1371b6c2d0050bf8 100644 GIT binary patch literal 64232 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 literal 31839 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 From 5b1bc93cdcdc4665338a0ac25607b6602675461c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E1=84=92=E1=85=A9=E1=86=BC=E1=84=89=E1=85=A5=E1=86=A8?= =?UTF-8?q?=E1=84=92=E1=85=A7=E1=86=AB?= Date: Tue, 11 Nov 2025 20:54:27 +0900 Subject: [PATCH 4/4] =?UTF-8?q?[Feature]=20=ED=83=80=EC=9D=B4=EB=A8=B8=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EC=8B=9C=20=EB=A1=9C=EC=BB=AC=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20=EB=B0=8F=20=EC=95=8C=EB=9E=8C=20=EA=B8=B0=EB=8A=A5?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TimerNotificationService 추가 (로컬 알림 예약/취소) - TimerState에 endDate 프로퍼티 추가 - TimerModel에서 endDate 관리 로직 추가 - 타이머 완료 시 시스템 사운드 및 진동 재생 - 타이머 완료 Alert 표시 기능 추가 - 타이머 무한 루프 버그 수정 (완료 시 stopTimer 호출) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Service/TimerNotificationService.swift | 88 +++++++++++++++++++ .../Timer/Sources/TimerContainer.swift | 45 +++++++++- .../Timer/Sources/TimerModel.swift | 5 ++ .../Timer/Sources/TimerState.swift | 5 +- .../Timer/Sources/TimerView.swift | 7 ++ 5 files changed, 145 insertions(+), 5 deletions(-) create mode 100644 Projects/Presentation/Timer/Sources/Service/TimerNotificationService.swift diff --git a/Projects/Presentation/Timer/Sources/Service/TimerNotificationService.swift b/Projects/Presentation/Timer/Sources/Service/TimerNotificationService.swift new file mode 100644 index 0000000..f8ab4a4 --- /dev/null +++ b/Projects/Presentation/Timer/Sources/Service/TimerNotificationService.swift @@ -0,0 +1,88 @@ +// +// TimerNotificationService.swift +// Timer +// +// Created by 홍석현 on 11/11/25. +// + +import Foundation +import UserNotifications + +public protocol TimerNotificationServiceProtocol { + func requestAuthorization() async -> Bool + func scheduleNotification(endDate: Date, totalTime: TimeInterval) + func cancelNotification() +} + +public final class TimerNotificationService: TimerNotificationServiceProtocol { + private let notificationCenter = UNUserNotificationCenter.current() + private let notificationIdentifier = "com.wakeyalarm.timer.completion" + + public init() {} + + // MARK: - Authorization + public func requestAuthorization() async -> Bool { + do { + let granted = try await notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) + if granted { + print("✅ 타이머 알림 권한 허용됨") + } else { + print("🚫 타이머 알림 권한 거부됨") + } + return granted + } catch { + print("❌ 타이머 알림 권한 요청 실패: \(error.localizedDescription)") + return false + } + } + + // MARK: - Schedule + public func scheduleNotification(endDate: Date, totalTime: TimeInterval) { + let content = UNMutableNotificationContent() + content.title = "⏰ 타이머 완료" + content.body = formatTimeForMessage(totalTime) + " 타이머가 완료되었습니다" + content.sound = .default + content.categoryIdentifier = "TIMER_COMPLETION" + + let timeInterval = endDate.timeIntervalSinceNow + + // 최소 1초 이상이어야 알림이 예약됨 + guard timeInterval > 1 else { + print("⚠️ 타이머 종료 시간이 너무 짧아 알림을 예약할 수 없습니다") + return + } + + let trigger = UNTimeIntervalNotificationTrigger(timeInterval: timeInterval, repeats: false) + let request = UNNotificationRequest(identifier: notificationIdentifier, content: content, trigger: trigger) + + notificationCenter.add(request) { error in + if let error = error { + print("❌ 타이머 알림 예약 실패: \(error.localizedDescription)") + } else { + print("✅ 타이머 알림 예약 완료: \(self.formatTimeForMessage(totalTime))") + } + } + } + + // MARK: - Cancel + public func cancelNotification() { + notificationCenter.removePendingNotificationRequests(withIdentifiers: [notificationIdentifier]) + notificationCenter.removeDeliveredNotifications(withIdentifiers: [notificationIdentifier]) + print("🗑️ 타이머 알림 취소됨") + } + + // MARK: - Helper + private func formatTimeForMessage(_ timeInterval: TimeInterval) -> String { + let hours = Int(timeInterval) / 3600 + let minutes = (Int(timeInterval) % 3600) / 60 + let seconds = Int(timeInterval) % 60 + + if hours > 0 { + return String(format: "%d시간 %d분 %d초", hours, minutes, seconds) + } else if minutes > 0 { + return String(format: "%d분 %d초", minutes, seconds) + } else { + return String(format: "%d초", seconds) + } + } +} diff --git a/Projects/Presentation/Timer/Sources/TimerContainer.swift b/Projects/Presentation/Timer/Sources/TimerContainer.swift index 19ed87f..26117ee 100644 --- a/Projects/Presentation/Timer/Sources/TimerContainer.swift +++ b/Projects/Presentation/Timer/Sources/TimerContainer.swift @@ -8,6 +8,8 @@ import Foundation import Combine +import UserNotifications +import AVFoundation // MARK: - Timer Container (ViewModel) public final class TimerContainer: ObservableObject { @@ -16,16 +18,28 @@ public final class TimerContainer: ObservableObject { // MARK: - Private Properties private let model: TimerModelProtocol + private let notificationService: TimerNotificationServiceProtocol private var cancellables = Set() private var timerCancellable: AnyCancellable? + private var audioPlayer: AVAudioPlayer? + + // MARK: - Published Alert State + @Published public var showCompletionAlert: Bool = false // MARK: - Initialization public init( model: TimerModelProtocol = TimerModel(), + notificationService: TimerNotificationServiceProtocol = TimerNotificationService(), initialState: TimerState = TimerState() ) { self.model = model + self.notificationService = notificationService self.state = initialState + + // 알림 권한 요청 + Task { + await notificationService.requestAuthorization() + } } // MARK: - Intent Processing @@ -43,15 +57,18 @@ public final class TimerContainer: ObservableObject { switch sideEffect { case .startTimerTicking: startTimer() + scheduleNotification() case .stopTimerTicking: stopTimer() + cancelNotification() case .playAlarm: playAlarm() + cancelNotification() case .showCompletionAlert: - showCompletionAlert() + displayCompletionAlert() } } @@ -71,6 +88,7 @@ public final class TimerContainer: ObservableObject { if self.state.remainingTime > 0 { self.send(.timerTick) } else { + self.stopTimer() self.send(.timerCompleted) } } @@ -84,17 +102,36 @@ public final class TimerContainer: ObservableObject { // MARK: - Alarm & Notifications private func playAlarm() { - // TODO: 알람 사운드 재생 + // 시스템 사운드 재생 (진동 포함) + AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate)) + AudioServicesPlaySystemSound(1005) // 알림 사운드 + print("🔔 타이머 완료! 알람 울림") + + // 완료 알림도 함께 표시 + displayCompletionAlert() } - private func showCompletionAlert() { - // TODO: 완료 알림 표시 + private func displayCompletionAlert() { + Task { @MainActor in + self.showCompletionAlert = true + } print("✅ 타이머 완료 알림") } + // MARK: - Notification Management + private func scheduleNotification() { + guard let endDate = state.endDate else { return } + notificationService.scheduleNotification(endDate: endDate, totalTime: state.totalTime) + } + + private func cancelNotification() { + notificationService.cancelNotification() + } + // MARK: - Cleanup deinit { stopTimer() + cancelNotification() } } diff --git a/Projects/Presentation/Timer/Sources/TimerModel.swift b/Projects/Presentation/Timer/Sources/TimerModel.swift index c94f82d..5a55072 100644 --- a/Projects/Presentation/Timer/Sources/TimerModel.swift +++ b/Projects/Presentation/Timer/Sources/TimerModel.swift @@ -40,20 +40,24 @@ public final class TimerModel: TimerModelProtocol { newState.remainingTime = TimeInterval(totalSeconds) newState.progress = 1.0 newState.timerStatus = .running + newState.endDate = Date().addingTimeInterval(TimeInterval(totalSeconds)) sideEffect = .startTimerTicking case .pauseTimer: newState.timerStatus = .paused + newState.endDate = nil sideEffect = .stopTimerTicking case .resumeTimer: newState.timerStatus = .running + newState.endDate = Date().addingTimeInterval(state.remainingTime) sideEffect = .startTimerTicking case .cancelTimer: newState.timerStatus = .idle newState.remainingTime = 0 newState.progress = 1.0 + newState.endDate = nil sideEffect = .stopTimerTicking case .timerTick: @@ -66,6 +70,7 @@ public final class TimerModel: TimerModelProtocol { newState.timerStatus = .idle newState.remainingTime = 0 newState.progress = 1.0 + newState.endDate = nil sideEffect = .playAlarm } diff --git a/Projects/Presentation/Timer/Sources/TimerState.swift b/Projects/Presentation/Timer/Sources/TimerState.swift index 8de9948..d277ada 100644 --- a/Projects/Presentation/Timer/Sources/TimerState.swift +++ b/Projects/Presentation/Timer/Sources/TimerState.swift @@ -20,6 +20,7 @@ public struct TimerState: Equatable { public var remainingTime: TimeInterval public var totalTime: TimeInterval public var progress: Double + public var endDate: Date? public init( selectedHours: Int = 0, @@ -28,7 +29,8 @@ public struct TimerState: Equatable { timerStatus: TimerStatus = .idle, remainingTime: TimeInterval = 0, totalTime: TimeInterval = 0, - progress: Double = 1.0 + progress: Double = 1.0, + endDate: Date? = nil ) { self.selectedHours = selectedHours self.selectedMinutes = selectedMinutes @@ -37,6 +39,7 @@ public struct TimerState: Equatable { self.remainingTime = remainingTime self.totalTime = totalTime self.progress = progress + self.endDate = endDate } } diff --git a/Projects/Presentation/Timer/Sources/TimerView.swift b/Projects/Presentation/Timer/Sources/TimerView.swift index fc08567..cea1969 100644 --- a/Projects/Presentation/Timer/Sources/TimerView.swift +++ b/Projects/Presentation/Timer/Sources/TimerView.swift @@ -61,6 +61,13 @@ public struct TimerView: View { .padding(.bottom, 60) } .animation(.easeInOut(duration: 0.3), value: container.state.timerStatus) + .alert("타이머 완료", isPresented: $container.showCompletionAlert) { + Button("확인", role: .cancel) { + container.showCompletionAlert = false + } + } message: { + Text("설정한 타이머가 완료되었습니다.") + } } }