LocalNotificationSender.swift 2.78 KB
import Foundation
import UIKit
import UserNotifications

enum LocalNotificationSenderError: LocalizedError {
    case notificationsDenied
    case notificationsUnsupportedStatus(Int)

    var errorDescription: String? {
        switch self {
        case .notificationsDenied:
            return "Notifications permission is denied."
        case .notificationsUnsupportedStatus(let status):
            return "Notifications permission status is unsupported: \(status)."
        }
    }
}

final class LocalNotificationSender {
    static let shared = LocalNotificationSender()

    private let center: UNUserNotificationCenter

    init(center: UNUserNotificationCenter = .current()) {
        self.center = center
    }

    func send(
        dataType: Int64,
        dateTime: Int64,
        title: String,
        body: String,
        link: String,
        onlyWhenAppNotActive: Bool = true
    ) async throws -> Bool {
        if onlyWhenAppNotActive, UIApplication.shared.applicationState == .active {
            return false
        }

        try await ensureNotificationPermission()

        let content = UNMutableNotificationContent()
        content.title = title
        content.body = body
        content.sound = .default
        content.userInfo = notificationUserInfo(
            link: link,
            dataType: dataType,
            dateTime: dateTime
        )

        let request = UNNotificationRequest(
            identifier: "doublefeel.local.\(UUID().uuidString)",
            content: content,
            trigger: UNTimeIntervalNotificationTrigger(timeInterval: 1, repeats: false)
        )
        try await center.add(request)
        return true
    }

    private func ensureNotificationPermission() async throws {
        let settings = await center.notificationSettings()
        switch settings.authorizationStatus {
        case .authorized, .provisional, .ephemeral:
            return
        case .notDetermined:
            let granted = try await center.requestAuthorization(options: [.alert, .sound, .badge])
            if !granted {
                throw LocalNotificationSenderError.notificationsDenied
            }
        case .denied:
            throw LocalNotificationSenderError.notificationsDenied
        @unknown default:
            throw LocalNotificationSenderError.notificationsUnsupportedStatus(
                settings.authorizationStatus.rawValue
            )
        }
    }

    private func notificationUserInfo(
        link: String,
        dataType: Int64,
        dateTime: Int64
    ) -> [String: Any] {
        var userInfo: [String: Any] = [
            "data_type": dataType,
            "date_time": dateTime,
        ]
        if !link.isEmpty {
            userInfo["url"] = link
            userInfo["link"] = link
        }
        return userInfo
    }
}