# 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

```swift
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

```swift
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](/docs/universal-push/credentials). Apple rejects sends whose topic (bundle id) does not match the key.

## 4. Send and verify

```bash
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`](/docs/universal-push/verification) — for iOS it returns APNs' answer immediately (`accepted`, or `BadDeviceToken` when the token no longer exists).

## Deregister on logout

```swift
// 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](/docs/universal-push/registration).
