iOS SDK (Swift)
The ReachBell Swift package is the iOS client SDK for ReachBell — the device counterpart to the browser SDK. It registers an iOS device for Apple Push Notification service (APNs), links it to your user id, and tags it for segmentation. Sending pushes and campaigns happens from your backend with the Node server SDK.
Requires iOS 13+ and Swift 5.7+ / Xcode 14+. You'll also need an Apple Developer account with the Push Notifications capability and an APNs key/certificate configured in your ReachBell dashboard.
Preview
This SDK is newly released. Build and test it inside a real iOS app on a physical device before shipping — the iOS Simulator does not receive remote pushes.
Install
Add the package in Xcode via File ▸ Add Package Dependencies… and enter the repository URL:
https://github.com/reachbell/reachbell-ios
Add the ReachBell product to your app target. Or, in Package.swift:
dependencies: [
.package(url: "https://github.com/reachbell/reachbell-ios", from: "1.0.0")
],
targets: [
.target(name: "YourApp", dependencies: [
.product(name: "ReachBell", package: "reachbell-ios")
])
]Setup
Do this once when you set up the project:
- Select your target ▸ Signing & Capabilities ▸ + Capability ▸ Push Notifications.
- Add the Background Modes capability and check Remote notifications if you send silent/content-available pushes.
- Test on a real device — the iOS Simulator does not receive remote pushes.
No Info.plist keys are required for basic remote push.
Forward the APNs token (required)
APNs delivers the device token to your AppDelegate, and the SDK cannot see it unless you forward it via setAPNsToken(_:). This step is mandatory — it's what triggers subscriber registration (POST /subscribers).
The SDK converts the raw Data token to a lowercase hex string, persists it (so identify/tag survive a relaunch), and registers the subscriber as { token, tokenType: "apns", platform: "ios", externalId? }.
UIKit (AppDelegate):
import UIKit
import ReachBell
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
ReachBell.shared.configure(apiKey: "rb_live_…")
// Optionally request authorization now (or defer to a better moment in your UX):
Task { try? await ReachBell.shared.requestAuthorizationAndRegister() }
return true
}
// Forward the APNs device token — this is what triggers POST /subscribers.
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
ReachBell.shared.setAPNsToken(deviceToken)
}
func application(
_ application: UIApplication,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("APNs registration failed: \(error)")
}
}SwiftUI (App lifecycle): SwiftUI's App has no APNs token callback, so attach an AppDelegate adaptor to forward it:
import SwiftUI
import ReachBell
class AppDelegate: NSObject, UIApplicationDelegate {
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
ReachBell.shared.setAPNsToken(deviceToken)
}
}
@main
struct MyApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
init() {
ReachBell.shared.configure(apiKey: "rb_live_…")
}
var body: some Scene {
WindowGroup {
ContentView()
.task {
try? await ReachBell.shared.requestAuthorizationAndRegister()
}
}
}
}Initialize
Configure the shared singleton once, early in launch, before any other call:
import ReachBell
ReachBell.shared.configure(apiKey: "rb_live_…")configure(apiKey:apiUrl:) takes an optional apiUrl that defaults to https://api.reachbell.com. Every request sends the x-api-key: <apiKey> header, matching the browser and server SDKs.
Register for push
Ask for notification permission and register for remote notifications. requestAuthorizationAndRegister(options:) is async throws and returns a Bool; options defaults to [.alert, .sound, .badge]:
Task {
do {
try await ReachBell.shared.requestAuthorizationAndRegister()
} catch {
print("ReachBell registration failed: \(error)")
}
}Once APNs calls back into your AppDelegate, setAPNsToken(_:) (wired up in Setup) performs the subscriber registration. The current device token is available as ReachBell.shared.apnsToken (a hex string, or nil).
Identify
Link the device to your user id. identify(_:) is async throws and calls POST /subscribers/identify { token, externalId }:
try await ReachBell.shared.identify("user_42")Calling identify(_:) before the APNs token arrives does not throw — the externalId is stashed and folded into the next setAPNsToken(_:) registration, so calling it right after requestAuthorizationAndRegister() is safe.
Tag
Tag the device for segmentation. tag(_:) is async throws and calls POST /subscribers/tag { token, tags }:
try await ReachBell.shared.tag(["pro", "ios"])tag(_:) requires a registered token and throws ReachBellError.noToken if none exists yet, so call it after registration completes.
Errors
Async/throwing methods throw a ReachBellError:
| Case | Meaning |
|---|---|
.notConfigured | configure(apiKey:) was not called. |
.notificationAuthorizationDenied | The user denied notification permission. |
.noToken | No APNs token yet — register for push first. |
.encodingFailed(Error) | The request body could not be JSON-encoded. |
.network(underlying:) | Transport failed (offline, timeout, TLS, …). |
.server(status:body:) | Non-2xx HTTP response; body is the raw payload. |
.invalidResponse | The response was not a valid HTTP response. |
do {
try await ReachBell.shared.identify("user_42")
} catch ReachBellError.noToken {
// Register for push first.
} catch ReachBellError.server(let status, _) {
print("HTTP \(status)")
} catch {
print(error.localizedDescription)
}