Skip to content

Commit

Permalink
feat/#245 로컬알람 구현 성공
Browse files Browse the repository at this point in the history
  • Loading branch information
hooni0918 committed Jul 19, 2024
1 parent 339f7fc commit 4e0bb2e
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 24 deletions.
3 changes: 3 additions & 0 deletions KkuMulKum/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {

UNUserNotificationCenter.current().delegate = LocalNotificationManager.shared

// KakaoSDK 초기화 과정에서 앱 키를 동적으로 불러오기
if let kakaoAppKey = fetchKakaoAppKeyFromPrivacyInfo() {
KakaoSDK.initSDK(appKey: kakaoAppKey)
Expand Down
54 changes: 45 additions & 9 deletions KkuMulKum/Resource/Notification/LocalNotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,67 @@ import Foundation

import UserNotifications

class LocalNotificationManager {

class LocalNotificationManager: NSObject, UNUserNotificationCenterDelegate {
static let shared = LocalNotificationManager()
private let notificationCenter = UNUserNotificationCenter.current()


private override init() {
super.init()
notificationCenter.delegate = self
}

func requestAuthorization(completion: @escaping (Bool) -> Void) {
notificationCenter.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
completion(granted)
if let error = error {
print("Authorization request error: \(error)")
}
DispatchQueue.main.async {
completion(granted)
}
}
}

func scheduleNotification(title: String, body: String, triggerDate: Date) {
func scheduleNotification(title: String, body: String, triggerDate: Date, identifier: String,
completion: @escaping (
Error?
) -> Void
) {
let content = UNMutableNotificationContent()
content.title = title
content.body = body
content.sound = .default

let components = Calendar.current.dateComponents([.year, .month, .day, .hour, .minute], from: triggerDate)
let components = Calendar.current.dateComponents(
[.year, .month,.day, .hour, .minute, .second ],
from: triggerDate
)
let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: false)

let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
let request = UNNotificationRequest(identifier: identifier, content: content, trigger: trigger)

notificationCenter.add(request) { error in
if let error = error {
print("Error scheduling notification: \(error)")
DispatchQueue.main.async {
completion(error)
}
}
}

func getPendingNotifications(completion: @escaping ([UNNotificationRequest]) -> Void) {
notificationCenter.getPendingNotificationRequests { requests in
DispatchQueue.main.async {
completion(requests)
}
}
}

func removeAllPendingNotifications() {
notificationCenter.removeAllPendingNotificationRequests()
}

func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
) {
completionHandler([.banner, .sound, .badge])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ final class SetReadyInfoViewController: BaseViewController {
fatalError("init(coder:) has not been implemented")
}


// MARK: - LifeCycle

override func viewWillAppear(_ animated: Bool) {
Expand All @@ -47,6 +48,7 @@ final class SetReadyInfoViewController: BaseViewController {
setupNavigationBarTitle(with: "준비 정보 입력하기")

bindViewModel()
setupTapGesture()
}

override func setupDelegate() {
Expand Down Expand Up @@ -95,6 +97,18 @@ final class SetReadyInfoViewController: BaseViewController {
private func doneButtonDidTap(_ sender: UIButton) {
viewModel.updateReadyInfo()
}


// MARK: - Keyboard Dismissal

private func setupTapGesture() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(dismissKeyboard))
view.addGestureRecognizer(tapGesture)
}

@objc private func dismissKeyboard() {
view.endEditing(true)
}
}


Expand Down Expand Up @@ -148,10 +162,9 @@ private extension SetReadyInfoViewController {
Toast().show(message: message, view: view, position: .bottom, inset: bottomInset)
}


// MARK: - Data Bind

func bindViewModel() {
func bindViewModel() {
viewModel.readyHour.bind { [weak self] readyHour in
self?.rootView.readyHourTextField.text = readyHour
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import UserNotifications

final class SetReadyInfoViewModel {
let promiseID: Int
Expand All @@ -32,7 +33,7 @@ final class SetReadyInfoViewModel {
promiseTime: String,
promiseName: String,
service: SetReadyStatusInfoServiceType,
notificationManager: LocalNotificationManager = LocalNotificationManager()
notificationManager: LocalNotificationManager = LocalNotificationManager.shared
) {
self.promiseID = promiseID
self.promiseName = promiseName
Expand Down Expand Up @@ -79,8 +80,7 @@ final class SetReadyInfoViewModel {
calculateTimes()
}

func checkValid(
readyHourText: String,
func checkValid(readyHourText: String,
readyMinuteText: String,
moveHourText: String,
moveMinuteText: String
Expand Down Expand Up @@ -122,25 +122,78 @@ final class SetReadyInfoViewModel {

private func scheduleLocalNotification() {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm"

guard let promiseDate = dateFormatter.date(from: promiseTime) else {
print("Invalid date format")
dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dateFormatter.locale = Locale(identifier: "ko_KR")
dateFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")

guard let promiseDate = dateFormatter.date(from: self.promiseTime) else {
print("Invalid date format: \(self.promiseTime)")
return
}

let totalPrepTime = TimeInterval((readyTime + moveTime) * 60)
let notificationDate = promiseDate.addingTimeInterval(-totalPrepTime)
let totalPrepTime = TimeInterval((self.readyTime + self.moveTime) * 60)

let timeFormatter = DateFormatter()
timeFormatter.dateFormat = "HH:mm:ss"
timeFormatter.timeZone = TimeZone(identifier: "Asia/Seoul")

print("약속 시간: \(timeFormatter.string(from: promiseDate))")
print("준비 시간: \(self.readyTime)")
print("이동 시간: \(self.moveTime)")
print("총 준비 시간: \(totalPrepTime / 60)")

let readyStartTime = promiseDate.addingTimeInterval(-TimeInterval(self.readyTime + self.moveTime) * 60)
let moveStartTime = promiseDate.addingTimeInterval(-TimeInterval(self.moveTime) * 60)

print("준비 시작 시간: \(timeFormatter.string(from: readyStartTime))")
print("이동 시작 시간: \(timeFormatter.string(from: moveStartTime))")

notificationManager.requestAuthorization { granted in
self.notificationManager.requestAuthorization { [weak self] granted in
guard let self = self else { return }
if granted {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("현재 알림 설정: \(settings)")
}

self.notificationManager.removeAllPendingNotifications()

self.notificationManager.scheduleNotification(
title: "준비 시작",
body: "\(self.promiseName) 약속 준비를 시작할 시간입니다!",
triggerDate: notificationDate
)
triggerDate: readyStartTime,
identifier: "readyStart_\(self.promiseID)"
) { error in
if let error = error {
print("준비 시작 알림 설정 실패: \(error)")
} else {
print("준비 시작 알림이 \(timeFormatter.string(from: readyStartTime))에 설정되었습니다.")
}
}

self.notificationManager.scheduleNotification(
title: "이동 시작",
body: "\(self.promiseName) 약속 장소로 이동할 시간입니다!",
triggerDate: moveStartTime,
identifier: "moveStart_\(self.promiseID)"
) { error in
if let error = error {
print("이동 시작 알림 설정 실패: \(error)")
} else {
print("이동 시작 알림이 \(timeFormatter.string(from: moveStartTime))에 설정되었습니다.")
}
}

self.notificationManager.getPendingNotifications { requests in
print("예정된 알림 수: \(requests.count)")
for request in requests {
if let trigger = request.trigger as? UNCalendarNotificationTrigger,
let nextTriggerDate = trigger.nextTriggerDate() {
print("알림 ID: \(request.identifier), 예정 시간: \(timeFormatter.string(from: nextTriggerDate))")
}
}
}
} else {
print("Notification permission not granted")
print("알림 권한이 허용되지 않았습니다.")
}
}
}
Expand Down

0 comments on commit 4e0bb2e

Please sign in to comment.