FI
ForgePush

ForgePushPermission

Request push authorization, check the current status, and deep-link into Settings.

PushPermission

A Sendable struct wrapping UNUserNotificationCenter. Methods: request(_:), status(), openSettings().

request(_:)

Presents the system authorization prompt. Defaults to [.alert, .badge, .sound]. Returns whether the user granted permission. Marked @discardableResult.

import ForgePushPermission

let permission = PushPermission()

// Request authorization (alert, badge, sound by default)
let granted = try await permission.request()
// Request with custom options
let granted = try await permission.request([.alert, .badge, .sound, .provisional])

status()

Returns the current UNAuthorizationStatus without triggering any prompts.

let permission = PushPermission()

switch await permission.status() {
case .authorized:
    print("Push is enabled")
case .denied:
    await permission.openSettings()
case .notDetermined:
    try await permission.request()
case .provisional:
    print("Provisional — quiet delivery")
case .ephemeral:
    print("App Clip context")
@unknown default:
    break
}

openSettings()

Opens the app's page in iOS Settings. Runs on @MainActor. Use this when the user has denied permission and needs to re-enable it manually.

let permission = PushPermission()

if await permission.status() == .denied {
    await permission.openSettings()
}

Note: PushPermission does not register for remote notifications. Use PushTokenManager.registerForRemoteNotifications() from ForgePushToken to trigger the APNs registration flow after the user grants permission.

Full Flow

A typical pattern that handles all authorization states:

TaskFlowNotificationsManager.swift
import ForgePushPermission
import ForgePushToken

final class TaskFlowNotificationsManager {
    private let permission = PushPermission()
    private let tokenManager: PushTokenManager

    init(tokenManager: PushTokenManager) {
        self.tokenManager = tokenManager
    }

    func enableNotifications() async throws {
        let status = await permission.status()

        switch status {
        case .notDetermined:
            try await permission.request()
            await tokenManager.registerForRemoteNotifications()
        case .authorized, .provisional:
            await tokenManager.registerForRemoteNotifications()
        case .denied:
            await permission.openSettings()
        default:
            break
        }
    }
}