
✅ 1. 화면이 열려 있을 때 (포그라운드 상태)
화면이 켜져 있고 타이머가 끝났을 때 알림창(Alert)이나 경고음(Sound)을 주려면, stopTimer() 호출 직후에 추가
@objc func refreshValue() {
guard let startDate = userDefaults.object(forKey: startDateKey) as? Date else { return }
let elapsed = Int(Date().timeIntervalSince(startDate))
let updatedRemaining = baseRemainingSeconds - elapsed
if updatedRemaining > 0 {
remainingSeconds = updatedRemaining
updateLabel()
} else {
stopTimer()
timeLabel.text = "00:00:00"
presentTimerFinishedAlert() // ✅ 여기에 추가
AudioServicesPlaySystemSound(SystemSoundID(1005)) // 📣 기본 경고음
}
}
func presentTimerFinishedAlert() {
let alert = UIAlertController(title: "타이머 종료", message: "읽기 시간이 완료되었습니다!", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "확인", style: .default))
present(alert, animated: true)
}
🛠️ AudioServicesPlaySystemSound(SystemSoundID(1005)) 이건 시스템 기본 효과음 중 하나
✅ 2. 앱이 백그라운드에 있을 때 (홈버튼/화면 끄기 등)
이때는 Local Notification(로컬 알림)을 등록
타이머가 끝나는 시점을 계산해서 미리 알림을 예약해놓는 방식
// ✅ 타이머 시작 시:
func scheduleLocalNotification(after seconds: Int) {
let content = UNMutableNotificationContent()
content.title = "타이머 종료"
content.body = "\(readItem.title) 읽기 시간이 끝났어요!"
content.sound = .default
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval(seconds), repeats: false)
let request = UNNotificationRequest(identifier: "timer_\(readItem.id)", content: content, trigger: trigger)
UNUserNotificationCenter.current().add(request, withCompletionHandler: nil)
}
// ✅ 타이머 시작 시에 호출
func startTimer() {
...
scheduleLocalNotification(after: remainingSeconds)
}
// ✅ 타이머 중단(PAUSE나 RESET) 시에는 등록된 알림을 취소
func stopTimer() {
...
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["timer_\(readItem.id)"])
}
🔐 알림 권한 요청은 필수
앱에서 알림을 사용하려면 최초에 권한을 요청
import UIKit
import CoreData
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// 🔔 알림 권한 요청
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
print("✅ 알림 권한 허용됨")
} else {
print("❌ 알림 권한 거부됨 또는 오류: \(error?.localizedDescription ?? "")")
}
}
return true
}
...
✅ NotificationPermissionManager.swift 파일 생성
앱 전체에서 깔끔하게 알림 권한을 관리할 수 있도록 유틸 클래스(Helper) 형식
이걸 활용하면 어디서든 간단하게 호출해서 알림 권한을 체크하고, 필요한 경우 설정화면으로 유도할 수 있음
import UIKit
import UserNotifications
struct NotificationPermissionManager {
/// 알림 권한 상태를 확인하고, 거부된 경우 설정 화면으로 유도
static func checkAndRequestPermission(from viewController: UIViewController) {
UNUserNotificationCenter.current().getNotificationSettings { settings in
switch settings.authorizationStatus {
case .notDetermined:
// 아직 한 번도 요청 안 함 → 요청
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
if granted {
print("🔔 알림 권한 허용됨")
} else {
print("❌ 알림 권한 거부됨 또는 오류: \(error?.localizedDescription ?? "")")
}
}
case .denied:
// 이미 거부된 상태 → 설정화면 유도
DispatchQueue.main.async {
let alert = UIAlertController(
title: "알림 권한이 꺼져있어요",
message: "설정 > 알림에서 권한을 켜주세요.",
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "설정으로 가기", style: .default) { _ in
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
})
alert.addAction(UIAlertAction(title: "취소", style: .cancel))
viewController.present(alert, animated: true)
}
case .authorized, .provisional, .ephemeral:
// 이미 권한 허용됨 → 아무것도 안 해도 됨
break
@unknown default:
break
}
}
}
}
'Project > 30MinRead' 카테고리의 다른 글
📌 Swift에서 독서 계획 내에 독서 메모를 CRUD하는 방법 (CoreData + Combine + MVVM) (1) | 2025.03.26 |
---|---|
📅 독서 시간을 완료한 날짜 저장하여 표시하기 (0) | 2025.03.24 |
🔥 시작일 ➡️ 종료일 포함하여 날짜 계산! (0) | 2025.03.21 |
❌ 날짜 잘못 선택하면.. 오류창 띄워야 하는데.. (0) | 2025.03.20 |
🤔 실시간.. 버튼 활성화 해보기 (Combine) (0) | 2025.03.19 |