103 lines
3.8 KiB
Swift
103 lines
3.8 KiB
Swift
import Foundation
|
|
import FirebaseMessaging
|
|
import UserNotifications
|
|
|
|
// MARK: - Notification Service
|
|
|
|
final class NotificationService: NSObject, @unchecked Sendable {
|
|
static let shared = NotificationService()
|
|
|
|
private override init() {
|
|
super.init()
|
|
}
|
|
|
|
func configure() {
|
|
UNUserNotificationCenter.current().delegate = self
|
|
registerForPushNotifications()
|
|
}
|
|
|
|
private func registerForPushNotifications() {
|
|
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
|
|
UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { granted, error in
|
|
guard granted else { return }
|
|
Task { @MainActor in
|
|
UIApplication.shared.registerForRemoteNotifications()
|
|
}
|
|
}
|
|
}
|
|
|
|
func updateFCMToken() {
|
|
if let token = Messaging.messaging().fcmToken {
|
|
Task {
|
|
await saveTokenToFirestore(token)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func saveTokenToFirestore(_ token: String) async {
|
|
guard let userId = try? FirestoreService.shared.userId() else { return }
|
|
|
|
let tokenRef = FirestoreService.shared.fcmTokensRef(userId).document(token)
|
|
try? await tokenRef.setData([
|
|
"token": token,
|
|
"updatedAt": FieldValue.serverTimestamp()
|
|
], merge: true)
|
|
}
|
|
}
|
|
|
|
// MARK: - UNUserNotificationCenterDelegate
|
|
|
|
extension NotificationService: UNUserNotificationCenterDelegate {
|
|
func userNotificationCenter(
|
|
_ center: UNUserNotificationCenter,
|
|
willPresent notification: UNNotification,
|
|
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
|
) {
|
|
completionHandler([.banner, .sound])
|
|
}
|
|
|
|
func userNotificationCenter(
|
|
_ center: UNUserNotificationCenter,
|
|
didReceive response: UNNotificationResponse,
|
|
withCompletionHandler completionHandler: @escaping () -> Void
|
|
) {
|
|
// Handle notification tap — deep link to relevant screen
|
|
let userInfo = response.notification.request.content.userInfo
|
|
handleDeepLink(userInfo)
|
|
completionHandler()
|
|
}
|
|
|
|
private func handleDeepLink(_ userInfo: [AnyHashable: Any]) {
|
|
guard let type = userInfo["type"] as? String else { return }
|
|
|
|
switch type {
|
|
case "daily_question", "gentle_reminder", "daily_question_reminder":
|
|
NotificationCenter.default.post(name: .navigateToDailyQuestion, object: nil)
|
|
case "partner_answered":
|
|
NotificationCenter.default.post(name: .navigateToReveal, object: userInfo["questionId"])
|
|
case "streak":
|
|
NotificationCenter.default.post(name: .navigateToHome, object: nil)
|
|
case "partner_started_game", "partner_completed_part", "partner_finished_game":
|
|
NotificationCenter.default.post(name: .navigateToPlay, object: nil)
|
|
case "memory_capsule_unlocked":
|
|
NotificationCenter.default.post(name: .navigateToMemoryLane, object: nil)
|
|
case "challenge_day_ready", "challenge_waiting":
|
|
NotificationCenter.default.post(name: .navigateToConnectionChallenges, object: nil)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Notification Names
|
|
|
|
extension Notification.Name {
|
|
static let navigateToDailyQuestion = Notification.Name("navigateToDailyQuestion")
|
|
static let navigateToReveal = Notification.Name("navigateToReveal")
|
|
static let navigateToHome = Notification.Name("navigateToHome")
|
|
static let navigateToPlay = Notification.Name("navigateToPlay")
|
|
static let navigateToMemoryLane = Notification.Name("navigateToMemoryLane")
|
|
static let navigateToConnectionChallenges = Notification.Name("navigateToConnectionChallenges")
|
|
}
|
|
|
|
typealias FieldValue = FirebaseFirestore.FieldValue |