iOS SDK reference
The Swift API surface — types, methods, and options.
#Installation
The Pushlane iOS SDK requires iOS 16+ and is distributed as a Swift Package (swift-tools-version 5.9). Add it in Xcode via File › Add Package Dependencies, or add it to your Package.swift:
// Package.swift — add to dependencies:
.package(url: "https://github.com/Big-Terence/pushlane-ios-sdk", from: "0.1.0")
// Add to your app target:
.product(name: "PushlaneCore", package: "pushlane-ios-sdk"),
.product(name: "PushlanePush", package: "pushlane-ios-sdk"),
.product(name: "PushlaneInApp", package: "pushlane-ios-sdk"),
// Add to your NotificationServiceExtension target only:
.product(name: "PushlaneNotificationService", package: "pushlane-ios-sdk"),| Product | Add to target | Purpose |
|---|---|---|
PushlaneCore | App target | Core facade: configure, identify, track. Pure Foundation, no UIKit. |
PushlanePush | App target | Push registration and open-tracking. |
PushlaneInApp | App target | Session tracking. Emits app_open and session_started. |
PushlaneNotificationService | NSE target only | Base class for your NotificationServiceExtension. Handles rich media and the received event. |
#Setup order
Wire the SDK in four calls at launch. Order matters — configure before identify, identify before track or register.
import PushlaneCore
import PushlanePush
import PushlaneInApp
@main
struct MyApp: App {
init() {
// 1. Configure — call before any track/identify call.
Pushlane.configure(
apiBase: URL(string: "https://<YOUR_WORKER>")!,
tenantId: "YOUR_TENANT_UUID",
publishableKey: "lpk_live_…", // from dashboard › Settings › API
appGroup: "group.com.example.myapp" // optional; needed for received events
)
// 2. Identify — set the user id before tracking events.
if let userId = Auth.current?.id {
Pushlane.identify(userId)
}
// 3. Session tracking + app_open event (once per cold launch).
PushlaneInApp.start()
// 4. Request push permission and register with APNs.
PushlanePush.register()
}
}#Pushlane.configure
| Parameter | Type | Notes |
|---|---|---|
| apiBase | URL | Base URL of your ingest worker. No trailing slash. Available in dashboard › Settings › API. |
| tenantId | String (UUID) | Your tenant UUID from dashboard › Settings › API. |
| publishableKey | String? (lpk_live_…) | Sent as Authorization: Bearer on every request. Required when INGEST_REQUIRE_KEY is on (default in production). |
| appGroup | String? | Shared App Group id. Required for the received deliverability event from the NSE. Must match PushlaneNotificationService.pushlaneAppGroup. |
The SDK auto-detects the APNs routing environment (sandbox vs. production) from the embedded provisioning profile and the install source (appstore / testflight / development) from the App Store receipt name. You never set these manually.
#Pushlane.identify / Pushlane.reset
Pushlane.identify() before PushlanePush.register() and before the first Pushlane.track() call. The SDK warns once in the Xcode console if a device token or a track call arrives before an identity is set, and the data is silently dropped until an id is available.// Set (or update) the current user — call as soon as the user id is known.
Pushlane.identify("user_42")
// Log out — detach the external id from this device.
Pushlane.reset()When appGroup is configured, Pushlane.identify mirrors the external id into the App Group so the NotificationServiceExtension (a separate OS process) can attribute the received event to the correct user.
#Pushlane.track
Send a named event with optional typed properties. The call is fire-and-forget — it returns immediately and retries up to 4 times with exponential backoff on 5xx or network errors.
// Zero properties
Pushlane.track("paywall_dismissed")
// With properties
Pushlane.track("paywall_viewed", [
"placement": "onboarding",
"paywall_id": "main",
])
// Mixed types
Pushlane.track("subscription_started", [
"plan": "annual",
"price_usd": 49.99,
"is_trial": false,
])Property values are typed as PushlaneValue. Swift literal syntax works directly in dictionary literals:
// Explicit construction
PushlaneValue.string("hello")
PushlaneValue.int(42)
PushlaneValue.double(3.14)
PushlaneValue.bool(true)
PushlaneValue.array([.string("a"), .string("b")])
// Literal shorthand (inferred automatically in dictionary literals)
let props: [String: PushlaneValue] = [
"name": "Alice", // .string
"score": 100, // .int
"ratio": 0.5, // .double
"paid": true, // .bool
]#Push registration
Call PushlanePush.register() once at launch. It requests notification permission and calls UIApplication.shared.registerForRemoteNotifications() on grant. Forward both AppDelegate callbacks:
// AppDelegate.swift (UIKit) or Scene lifecycle equivalent
func application(
_ app: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken token: Data
) {
PushlanePush.didRegister(deviceToken: token)
}
func application(
_ app: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
PushlanePush.didFailToRegister(error: error)
}PushlanePush.didRegister(deviceToken:) posts the hex-encoded token, the auto-detected APNs environment, and the current app and OS versions to /v1/register.
embedded.mobileprovision, not from #if DEBUG. TestFlight builds are compiled in Release mode but use the sandbox APNs gateway. The SDK handles this correctly; a #if DEBUG check does not.Open tracking is installed automatically when you call PushlanePush.register(). The SDK becomes the UNUserNotificationCenter delegate and emits an opened event carrying message_id, flow_id, and node_id from the push payload on every tap, including cold-launch taps.
#Session tracking
PushlaneInApp.start() must be called once at launch (after configure and identify). It does two things:
- Emits
app_openexactly once per cold launch of the process. Not re-emitted on foreground return. - Hooks UIKit lifecycle notifications. When the app returns to the foreground after 30 seconds or more in the background, it emits
session_startedwith a freshsession_id.
Both events carry SDK version, OS version, app version, APNs environment, install source, timezone, and locale automatically via the event context.
#Deliverability — the received event
received is emitted by the NotificationServiceExtension — a separate OS process that intercepts push payloads before they are displayed. It fires even when the user never opens the notification, giving you delivery data independent of open rates.
Create a NotificationServiceExtension target in Xcode, then subclass PushlaneNotificationService:
// NotificationService.swift — in your NSE Xcode target
import PushlaneNotificationService
final class NotificationService: PushlaneNotificationService {
// Return the SAME App Group id you passed to Pushlane.configure(appGroup:)
override var pushlaneAppGroup: String? {
"group.com.example.myapp"
}
}<!-- NSE target Info.plist — present in the Xcode template -->
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>Pushlane.configure(appGroup:) and pushlaneAppGroup. Without the shared App Group, rich media download still works but no received event is emitted.PushlaneNotificationService also downloads the attachment URL from the image_url or loop_media key and attaches it as a UNNotificationAttachment, with correct file extension inference from the MIME type.
#RevenueCat link
Pushlane attributes RevenueCat revenue events (trial started, renewal, cancellation, etc.) to a Pushlane user only when RevenueCat's appUserID exactly matches the id passed to Pushlane.identify. Use the convenience method to keep both in sync at a single call site:
// Identify for Pushlane AND pass the same id to RevenueCat.
// Both SDKs must use the SAME userId or revenue events won't reach your flows.
let uid = Pushlane.identifyForRevenueCat(currentUser.id)
Purchases.configure(withAPIKey: "appl_…", appUserID: uid)Pushlane.identifyForRevenueCat(_:) calls Pushlane.identify and returns the id unchanged. It has no compile-time dependency on the RevenueCat SDK.
#Auto-emitted events
These events are emitted by the SDK. Do not instrument them manually.
| Event | Emitted by | When |
|---|---|---|
app_open | PushlaneInApp.start() | Once per cold launch of the process. |
session_started | PushlaneInApp (lifecycle observer) | Foreground return after ≥30 s background. |
opened | PushlanePush (UNUserNotificationCenterDelegate) | User taps a push notification (cold-launch or foreground). |
received | PushlaneNotificationService (NSE) | Push payload intercepted by the NSE before display. |
push_registration_failed | PushlanePush.didFailToRegister | APNs returned an error during registration. |
Next: AI-agent install (MCP) →