114 lines
3.0 KiB
Swift
114 lines
3.0 KiB
Swift
import Foundation
|
|
import UserNotifications
|
|
|
|
@MainActor
|
|
final class NativeNotificationManager: NSObject, UNUserNotificationCenterDelegate {
|
|
static let shared = NativeNotificationManager()
|
|
|
|
private let center = UNUserNotificationCenter.current()
|
|
private var activationHandler: ((String) -> Void)?
|
|
|
|
private override init() {
|
|
super.init()
|
|
}
|
|
|
|
func start() {
|
|
center.delegate = self
|
|
}
|
|
|
|
func setActivationHandler(_ handler: @escaping (String) -> Void) {
|
|
activationHandler = handler
|
|
}
|
|
|
|
func setBadgeCount(_ count: Int) {
|
|
Task {
|
|
_ = await requestAuthorizationIfNeeded()
|
|
do {
|
|
try await center.setBadgeCount(max(0, count))
|
|
} catch {
|
|
NSLog("[omnigent] failed to set badge count: \(String(describing: error))")
|
|
}
|
|
}
|
|
}
|
|
|
|
func notify(title: String, body: String?, navigatePath: String?) {
|
|
Task {
|
|
let granted = await requestAuthorizationIfNeeded()
|
|
guard granted else { return }
|
|
|
|
let content = UNMutableNotificationContent()
|
|
content.title = title
|
|
content.body = body ?? ""
|
|
content.sound = .default
|
|
if let navigatePath, navigatePath.starts(with: "/") {
|
|
content.userInfo = ["navigatePath": navigatePath]
|
|
}
|
|
|
|
let request = UNNotificationRequest(
|
|
identifier: "omnigent.\(UUID().uuidString)",
|
|
content: content,
|
|
trigger: nil
|
|
)
|
|
|
|
do {
|
|
try await center.add(request)
|
|
} catch {
|
|
NSLog("[omnigent] failed to add notification: \(String(describing: error))")
|
|
}
|
|
}
|
|
}
|
|
|
|
nonisolated func userNotificationCenter(
|
|
_ center: UNUserNotificationCenter,
|
|
willPresent notification: UNNotification,
|
|
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void
|
|
) {
|
|
completionHandler([.banner, .list, .sound])
|
|
}
|
|
|
|
nonisolated func userNotificationCenter(
|
|
_ center: UNUserNotificationCenter,
|
|
didReceive response: UNNotificationResponse,
|
|
withCompletionHandler completionHandler: @escaping () -> Void
|
|
) {
|
|
let path = response.notification.request.content.userInfo["navigatePath"] as? String
|
|
Task { @MainActor in
|
|
if let path, path.starts(with: "/") {
|
|
activationHandler?(path)
|
|
}
|
|
completionHandler()
|
|
}
|
|
}
|
|
|
|
private func requestAuthorizationIfNeeded() async -> Bool {
|
|
#if DEBUG
|
|
guard !ProcessInfo.processInfo.isOmnigentScreenshotRun else { return false }
|
|
#endif
|
|
|
|
let settings = await center.notificationSettings()
|
|
switch settings.authorizationStatus {
|
|
case .authorized, .provisional, .ephemeral:
|
|
return true
|
|
case .denied:
|
|
return false
|
|
case .notDetermined:
|
|
do {
|
|
return try await center.requestAuthorization(options: [.alert, .sound, .badge])
|
|
} catch {
|
|
return false
|
|
}
|
|
@unknown default:
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
extension ProcessInfo {
|
|
fileprivate var isOmnigentScreenshotRun: Bool {
|
|
environment["OMNIGENT_SCREENSHOT_APP_URL"] != nil
|
|
|| arguments.contains("-FASTLANE_SNAPSHOT")
|
|
}
|
|
}
|
|
#endif
|