Skip to content

stefanprojchev/ForgePush

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ForgePush

Push notification management for iOS — permissions, tokens, and routing.

Swift 6.3+ iOS 18+ macOS 15+ License Release

📖 Full documentation →


ForgePush splits push notification handling into four focused libraries you can adopt individually. Drop in the ones you need, skip the ones you don't.

Libraries

Library Purpose
ForgePushPermission Request, check, and observe UNUserNotificationCenter authorization.
ForgePushToken PushTokenManager bridges APNs delegate callbacks into an async token stream.
ForgeSilentPush SilentPushRouter dispatches background pushes to handlers via TaskGroup.
ForgeVisiblePush VisiblePushRouter dispatches tapped notifications to matching handlers.
ForgePush Umbrella that re-exports all four.

Features

  • Handler-based routing — define small, focused handlers; the router dispatches matching pushes to them concurrently
  • Result aggregationSilentPushRouter aggregates handler results using the most-optimistic policy (.newData > .failed > .noData)
  • Async token stream — subscribe to tokenStream for the latest device token, no delegate callback wiring
  • Dependency-injected observers — routers take ConnectivityObserving and ProtectedDataObserving as dependencies, so you can wire ForgeObservers or provide your own
  • Thread-safePushTokenManager state is backed by LockedState; continuation lifecycle is deterministic

Requirements

  • iOS 18+
  • macOS 15+
  • Swift 6.3+ (Xcode 26 or later)

Installation

Xcode

  1. File → Add Package Dependencies…
  2. Paste https://github.com/stefanprojchev/ForgePush.git
  3. Set rule to Up to Next Major from 1.0.0

Package.swift

dependencies: [
    .package(url: "https://github.com/stefanprojchev/ForgePush.git", from: "1.0.0")
],
targets: [
    .target(
        name: "YourApp",
        dependencies: [
            // All four libraries
            "ForgePushPermission",
            "ForgePushToken",
            "ForgeSilentPush",
            "ForgeVisiblePush",
            // — or only what you need —
        ]
    )
]

Quick Start

Request permission

import ForgePushPermission

let permission = PushPermission()
let granted = try await permission.request()
if granted {
    await UIApplication.shared.registerForRemoteNotifications()
}

Observe the APNs token

import ForgePushToken

let tokenManager = PushTokenManager()

// In AppDelegate:
func application(_ app: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken token: Data) {
    tokenManager.didRegister(deviceToken: token)
}

// Anywhere:
Task {
    for await token in tokenManager.tokenStream {
        guard let token else { continue }
        await uploadTokenToServer(token)
    }
}

Route silent pushes

import ForgeSilentPush

struct SyncHandler: SilentPushHandler {
    let id = "sync"

    func matchesPayload(_ payload: [AnyHashable: Any]) -> Bool {
        (payload["kind"] as? String) == "sync"
    }

    func handle(_ payload: [AnyHashable: Any], context: SilentPushContext) async -> SilentPushResult {
        guard context.connectivity.isConnected else { return .noData }
        let newItems = try? await syncService.fetchNew()
        return (newItems?.isEmpty == false) ? .newData : .noData
    }
}

let router = SilentPushRouter(
    connectivity: ConnectivityObserver(),
    protectedData: ProtectedDataObserver()
)
router.addHandler(SyncHandler())

// In AppDelegate:
func application(
    _ app: UIApplication,
    didReceiveRemoteNotification userInfo: [AnyHashable: Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
) {
    router.handlePush(payload: userInfo, completionHandler: completionHandler)
}

Route tapped notifications

import ForgeVisiblePush

struct DeepLinkHandler: VisiblePushHandler {
    let id = "deep-link"

    func matches(_ response: UNNotificationResponse) -> Bool {
        response.notification.request.content.userInfo["link"] != nil
    }

    func handle(_ response: UNNotificationResponse, context: VisiblePushContext) async {
        if let url = response.notification.request.content.userInfo["link"] as? String {
            await Router.shared.open(url)
        }
    }
}

let router = VisiblePushRouter(
    connectivity: ConnectivityObserver(),
    protectedData: ProtectedDataObserver()
)
router.addHandler(DeepLinkHandler())

// In UNUserNotificationCenterDelegate:
func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
) {
    router.handleResponse(response, completionHandler: completionHandler)
}

Documentation

The Forge Family

ForgePush is part of the Forge family of Swift packages for iOS.

Package Description
ForgeCore Thread-safe primitives for iOS Swift packages.
ForgeInject Dependency injection with constructor and property wrapper support.
ForgeObservers Reactive system observers — connectivity, lifecycle, keyboard, and more.
ForgeStorage Type-safe key-value, file, and Keychain storage.
ForgeOrchestrator Orchestrate app flows — startup gates, data pipelines, and continuous monitors.
ForgePush Push notification management — permissions, tokens, and routing.
ForgeLocation Location triggers — geofencing, significant changes, and visits.
ForgeBackgroundTasks Background task scheduling and dispatch.

License

ForgePush is released under the MIT License. See LICENSE.

About

Push notification management for iOS — permissions, tokens, and routing.

Topics

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages