APNs Push
Register APNs device tokens with Gleam and route Gleam notification payloads back to the Portal.
What Your App Owns
The host app owns notification permission, APNs capability setup, UNUserNotificationCenterDelegate wiring, and foreground presentation policy.
Gleam owns project-scoped device token registration, unregistering stale tokens, and converting Gleam payloads into Portal URLs.
Before registering tokens
- Enable Push Notifications in the app target.
- Request notification authorization from the user.
- Configure APNs credentials in Gleam dashboard Settings -> Feature -> iOS Push (APNs).
- Make sure the SDK session is authenticated.
Configure APNs In The Gleam Dashboard
Open the Gleam dashboard, select the project, then go to Settings -> Feature -> iOS Push (APNs). Only project owners can save these credentials.
The iOS app never receives the APNs private key. The dashboard values let the Gleam backend create a token-based APNs connection, find compatible device tokens, and send notifications for this project.
| Dashboard field | Where to get it | What Gleam uses it for |
|---|---|---|
| APNs Key ID | Apple Developer -> Certificates, Identifiers & Profiles -> Keys -> your APNs Auth Key detail page. Copy the 10-character Key ID. | Identifies the signing key in the APNs provider token. |
| Apple Team ID | Apple Developer -> Membership details -> Team ID. | Identifies the Apple Developer team that owns the APNs key. |
| Bundle ID | Xcode -> Target -> General -> Bundle Identifier, or Info.plist CFBundleIdentifier. It must match the app that registers device tokens. | Used as the APNs topic and to filter registered device tokens by app bundle. |
| .p8 private key PEM | Apple Developer -> Keys -> create an Apple Push Notifications service key -> Download. Apple only shows this download once. | Signs the backend APNs provider token. Do not put this file in the iOS app. |
| APNs environment | Choose the environment that matches how the app is signed and distributed. | Selects the APNs host: sandbox for development delivery, production for distribution delivery. |
Create an APNs key
Use an Apple Push Notifications service key. Download the .p8 once and store it securely.
View setup stepsFind the Key ID
After creating the APNs key, Apple shows the Key ID next to the key name in Certificates, Identifiers & Profiles.
View setup stepsFind the Team ID
Copy the Team ID from Apple Developer membership details.
View setup stepsConfirm the Bundle ID
Use the same reverse-DNS bundle identifier as the iOS target that calls the Gleam SDK.
View setup stepsToken-based APNs
Gleam uses your Key ID, Team ID, Bundle ID, and .p8 key to connect to APNs from the backend.
View setup stepsDo not ship APNs credentials in the app
The iOS app only registers APNs device tokens with Gleam. APNs Key ID, Team ID, Bundle ID, private key, and environment are backend configuration values in the Gleam dashboard.
Register Device Tokens
After the dashboard is configured and the user has granted notification permission, forward the APNs device token to Gleam when iOS calls didRegisterForRemoteNotificationsWithDeviceToken.
The SDK sends the token with the current Gleam session and the app bundle ID. Gleam stores it project-scoped so replies and announcements can target the right user and device.
Register APNs token
SwiftUI apps still receive APNs callbacks through UIApplicationDelegate; install the delegate with UIApplicationDelegateAdaptor.
import SwiftUI
import UIKit
import UserNotifications
import GleamSDK
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
final class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
guard granted else { return }
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
return true
}
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
Task {
do {
try await Gleam.shared.registerDeviceToken(deviceToken)
} catch {
print("Gleam push registration failed: \(error)")
}
}
}
}Route Notification Payloads
When a user taps a Gleam notification, ask the SDK to turn the payload into a Portal URL, then present that URL through your existing Portal flow.
Handle tapped notification
The delegate callback is the same in SwiftUI and UIKit. If you keep the Portal in-app, replace openURL with your existing authenticated Portal presenter.
import SwiftUI
import UIKit
import UserNotifications
import GleamSDK
@MainActor
final class GleamPushRouter: ObservableObject {
@Published var portalURL: URL?
}
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
@StateObject private var pushRouter = GleamPushRouter()
var body: some Scene {
WindowGroup {
RootView()
.environmentObject(pushRouter)
.onAppear { AppDelegate.pushRouter = pushRouter }
}
}
}
struct RootView: View {
@EnvironmentObject private var pushRouter: GleamPushRouter
@Environment(\.openURL) private var openURL
var body: some View {
ContentView()
.onChange(of: pushRouter.portalURL) { _, url in
guard let url else { return }
openURL(url)
pushRouter.portalURL = nil
}
}
}
final class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
static weak var pushRouter: GleamPushRouter?
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
UNUserNotificationCenter.current().delegate = self
return true
}
func userNotificationCenter(
_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse
) async {
guard let url = try? Gleam.shared.url(
forRemoteNotification: response.notification.request.content.userInfo
) else {
return
}
await MainActor.run {
Self.pushRouter?.portalURL = url
}
}
}Unregister On Logout
When the current app user changes, unregister the previous token before clearing the SDK session. This prevents the old user from receiving future project notifications on this device.
Logout cleanup
try await Gleam.shared.unregisterCurrentDeviceToken()
try Gleam.shared.clearSession()