@@ -33,6 +33,7 @@ final class AlarmManager {
3333 private var shouldKeepBackgroundAudio = false
3434 private var isPreviewing = false
3535 private var hapticEngine : CHHapticEngine ?
36+ private var autoStopWorkItem : DispatchWorkItem ?
3637
3738 // MARK: - Init
3839 private init ( ) {
@@ -71,6 +72,8 @@ final class AlarmManager {
7172 /// 서버에서 받은 출발 시각 기준으로 1분 전에 반복 푸시/사운드/진동을 시작
7273 func startAlarm( title: String , body: String ) {
7374 // 기존 알람 상태만 정리 (silent는 유지 or 다시 켜기)
75+ autoStopWorkItem? . cancel ( )
76+ autoStopWorkItem = nil
7477 stopAlarm ( keepSilent: true )
7578 ensureBackgroundSilentRunning ( )
7679 applySavedVolumeForAlarmStart ( )
@@ -121,6 +124,7 @@ final class AlarmManager {
121124 }
122125
123126 print ( " 알람 종료 (keepSilent = \( keepSilent) ) " )
127+ UNUserNotificationCenter . current ( ) . removePendingNotificationRequests ( withIdentifiers: [ AlarmNotificationID . autoStopInfo] )
124128 }
125129
126130 func alarmInit( ) {
@@ -220,13 +224,19 @@ extension AlarmManager {
220224
221225 self . sendImmediateLocalPush ( title: title, body: body)
222226 }
227+
228+ scheduleAutoStop ( )
223229 }
224230
225- func sendImmediateLocalPush( title: String , body: String ) {
231+ func sendImmediateLocalPush( title: String , body: String , playSound : Bool = false ) {
226232 let content = UNMutableNotificationContent ( )
227233 content. title = title
228234 content. body = body
229- content. sound = nil // 사운드는 직접 재생 중
235+ if playSound {
236+ content. sound = . default
237+ } else {
238+ content. sound = nil
239+ }
230240
231241 let request = UNNotificationRequest (
232242 identifier: UUID ( ) . uuidString,
@@ -593,3 +603,37 @@ extension AlarmManager {
593603 print ( " 미리듣기 종료 (keep= \( shouldKeepBackgroundAudio) ) " )
594604 }
595605}
606+
607+ extension AlarmManager {
608+ private func scheduleAutoStop( ) {
609+ autoStopWorkItem? . cancel ( )
610+
611+ let content = UNMutableNotificationContent ( )
612+ content. title = " 출발 알람이 자동 종료되었어요 "
613+ content. body = " 클릭해서 경로 재탐색하기 "
614+ content. sound = . default
615+
616+ let request = UNNotificationRequest (
617+ identifier: AlarmNotificationID . autoStopInfo,
618+ content: content,
619+ trigger: UNTimeIntervalNotificationTrigger ( timeInterval: 120.0 , repeats: false )
620+ )
621+ UNUserNotificationCenter . current ( ) . add ( request, withCompletionHandler: nil )
622+
623+ // 2. 앱 내부의 정지 로직 및 팝업 신호 (포그라운드일 때 즉시, 백그라운드면 켜질 때 실행됨)
624+ let workItem = DispatchWorkItem { [ weak self] in
625+ guard let self = self else { return }
626+
627+ // 음악/진동 정지
628+ self . stopAlarm ( keepSilent: false ) // 아예 무음까지 끄기
629+
630+ UserDefaults . standard. set ( true , forKey: " isAlarmTimedOut " )
631+
632+ // 메인 뷰에 타임아웃 팝업 띄우라고 신호
633+ NotificationCenter . default. post ( name: NSNotification . Name ( " alarmDidTimeout " ) , object: nil )
634+ }
635+
636+ autoStopWorkItem = workItem
637+ DispatchQueue . main. asyncAfter ( deadline: . now( ) + 120.0 , execute: workItem)
638+ }
639+ }
0 commit comments