Swift Recipe (native iOS)

Register a native iOS app's APNs device token with Native Notify universal push from Swift — delegate, hex token, URLSession registration, and the Apple credentials it needs.

Native iOS apps talk to APNs directly; the device token that arrives in the app delegate is exactly what the universal service registers.

1. Ask for permission and register with APNs

import UIKit
import UserNotifications

@main
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    func application(_ application: UIApplication,
                     didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> 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) {
        let apnsToken = deviceToken.map { String(format: "%02x", $0) }.joined()
        Task { await UniversalPush.register(appId: 123, appToken: "yourAppToken", apnsToken: apnsToken) }
    }
}

APNs rotates device tokens on reinstall/restore and can re-deliver this callback at any launch — always re-register with the same deviceId.

2. Register with Native Notify

import Foundation

enum UniversalPush {
    static func register(appId: Int, appToken: String, apnsToken: String, subscriberId: String? = nil) async {
        guard let url = URL(string: "https://app.nativenotify.com/api/universal/device/register") else { return }

        let deviceId = DeviceIdentity.stableKey()   // persist it — see below
        var body: [String: Any] = [
            "appId": appId,
            "appToken": appToken,
            "deviceId": deviceId,
            "platform": "ios",
            "tokens": ["apnsToken": apnsToken],
        ]
        if let subscriberId { body["subscriberId"] = subscriberId }

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: body)

        _ = try? await URLSession.shared.data(for: request)
    }
}

For deviceId, UIDevice.current.identifierForVendor works (persist it in the keychain so it survives reinstalls) — or generate a UUID the first time and store it the same way.

3. Credentials the app needs

Save the app's Apple .p8 key with its Key ID, Team ID and the app's Bundle ID as the app's credentials in Native Notify — Push Credentials. Apple rejects sends whose topic (bundle id) does not match the key.

4. Send and verify

curl -X POST https://app.nativenotify.com/api/universal/notifications/send \
  -H "Content-Type: application/json" \
  -d '{"appId":123,"appToken":"yourAppToken","title":"Hello iOS","message":"Delivered over APNs.","audience":{"type":"all"}}'

Then confirm the individual device answered with test-send — for iOS it returns APNs' answer immediately (accepted, or BadDeviceToken when the token no longer exists).

Deregister on logout

// POST https://app.nativenotify.com/api/universal/device/deregister
// body: { "appId": 123, "appToken": "yourAppToken", "deviceId": "…" }

Deregister is a hard delete (idempotent — safe to call more than once). See Device Registration.