FI
ForgePush

ForgePushToken

Bridge the APNs device token callbacks from AppDelegate into a simple manager with a reactive async stream.

PushTokenManager

A Sendable class that owns the device token lifecycle. Wire it into your AppDelegate callbacks and consume the token from anywhere in your app.

AppDelegate Wiring

Forward the two APNs registration delegate methods to PushTokenManager:

AppDelegate.swift
import UIKit
import ForgePushToken

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    let tokenManager = PushTokenManager()

    func application(_ application: UIApplication,
        didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        tokenManager.didRegister(deviceToken: deviceToken)
    }

    func application(_ application: UIApplication,
        didFailToRegisterForRemoteNotificationsWithError error: Error) {
        tokenManager.didFailToRegister(error: error)
    }
}

registerForRemoteNotifications()

Calls UIApplication.shared.registerForRemoteNotifications() on the main actor. Triggers the APNs registration flow, which delivers the device token (or an error) to your AppDelegate. Available via a UIKit extension on PushTokenManager.

import ForgePushToken

let tokenManager = PushTokenManager()

// Call after the user grants permission.
// Triggers didRegisterForRemoteNotificationsWithDeviceToken
// or didFailToRegisterForRemoteNotificationsWithError in AppDelegate.
await tokenManager.registerForRemoteNotifications()

token

The most recently registered APNs device token as a hex string. nil until the first successful registration or after a registration failure (only cleared if a token was previously set).

import ForgePushToken

let tokenManager = PushTokenManager()

// Access the most recently registered token (nil until registered)
if let token = tokenManager.token {
    await uploadToken(token)
}

tokenStream

An AsyncStream<String?> that emits the current token on subscription and each time it changes. The value is optional — it emits nil when registration fails (and a token was previously set). Use this to reactively upload the token to your server whenever it changes.

import ForgePushToken

let tokenManager = PushTokenManager()

// Observe token updates with structured concurrency
// tokenStream is AsyncStream<String?> — emits nil on failure
for await token in tokenManager.tokenStream {
    if let token {
        await uploadToken(token)
    } else {
        await clearServerToken()
    }
}

Note: APNs can issue a new device token at any time — for example, after a restore from backup or when the app is reinstalled. Always observe tokenStream rather than reading token once at startup.

didRegister(deviceToken:)

Call this from application(_:didRegisterForRemoteNotificationsWithDeviceToken:). Converts the raw Data token to a hex string and publishes it to tokenStream.

didFailToRegister(error:)

Call this from application(_:didFailToRegisterForRemoteNotificationsWithError:). Logs the error. If a token was previously set, clears it to nil and emits nil on tokenStream. If no token was set, no stream emission occurs.

Example — Upload on Change

TaskFlowTokenUploadService.swift
import ForgePushToken

final class TaskFlowTokenUploadService {
    private let tokenManager: PushTokenManager

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

    func startObserving() {
        Task {
            for await token in tokenManager.tokenStream {
                if let token {
                    try? await api.registerDevice(token: token)
                }
            }
        }
    }
}