iOS
Ensure you have completed the Prerequisites section before continuing.
Installation
The minimum supported version for iOS is 13.0.
To set the Minimum SDK target:
- Open your iOS project in Xcode
- Select your Target > General > Deployment Info > Ensure that the version is set to 13.0+
Swift Package Manager
- In your Xcode project, go to File > Add Package Dependencies…
- Enter the repository URL:
https://github.com/dashxhq/dashx-ios.git
- Add the DashX library to your app target. If you use push notifications / Firebase integration helpers, also add DashXFirebase.
A CocoaPods spec publishes the core DashX library only. The DashXFirebase helper (for DashXAppDelegate, etc.) is available through Swift Package Manager alongside DashX from the same repository.
Configuration
DashX needs to be initialized as early as possible in your application's lifecycle, which can be done in the AppDelegate class within the application(_:didFinishLaunchingWithOptions:) method:
import DashX
// ...
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DashX.configure(
withPublicKey: "...", // required
baseURI: "...", // optional
targetEnvironment: "..." // optional
)
// ...
return true
}
Your Public Key is not sensitive, and can be stored in code or within Info.plist. For several environments, prefer xcconfig-driven values per build configuration rather than hard-coding secrets in Swift.
Multiple environments with xcconfig
If you use multiple Environments, a common pattern is:
- Add
.xcconfigfiles (for example under aConfig/group in your repo). Use a shared file per environment for real values, and debug / release files that#includethe shared file so Debug and Release stay in sync except where they differ. - Define build settings such as
DASHX_PUBLIC_KEY,DASHX_BASE_URI, andDASHX_TARGET_ENVIRONMENT. Standard.xcconfigsyntax cannot include//inside URLs; use a known workaround (e.g.https:/$()/api.example.com/graphql). - Wire configurations in Xcode: Project → Info → Configurations → assign each Debug / Release row to the correct
.xcconfigfor that environment. - Expose values to Swift via
Info.plistso they are visible toBundle.mainat runtime:
<key>DASHX_PUBLIC_KEY</key>
<string>$(DASHX_PUBLIC_KEY)</string>
<key>DASHX_BASE_URI</key>
<string>$(DASHX_BASE_URI)</string>
<key>DASHX_TARGET_ENVIRONMENT</key>
<string>$(DASHX_TARGET_ENVIRONMENT)</string>
- Read keys in code (same pattern as NSHipster: xcconfig in Swift):
enum Configuration {
enum Error: Swift.Error { case missingKey, invalidValue }
static func value<T>(for key: String) throws -> T where T: LosslessStringConvertible {
guard let object = Bundle.main.object(forInfoDictionaryKey: key) else {
throw Error.missingKey
}
switch object {
case let value as T: return value
case let string as String:
guard let value = T(string) else { fallthrough }
return value
default: throw Error.invalidValue
}
}
}
Then pass them into DashX.configure:
DashX.configure(
withPublicKey: try! Configuration.value(for: "DASHX_PUBLIC_KEY"),
baseURI: try? Configuration.value(for: "DASHX_BASE_URI"),
targetEnvironment: try? Configuration.value(for: "DASHX_TARGET_ENVIRONMENT")
)
For a complete, working app that wires xcconfig, Info.plist, and DashX.configure together, see the dashx-demo-ios repository (e.g. Config/, Utils/Configuration.swift, and AppDelegate).
Optional: lifecycle tracking
To automatically track app installed/updated/opened events and session length:
DashX.enableLifecycleTracking()
Optional: ad tracking
To request App Tracking Transparency permission and enable IDFA collection:
DashX.enableAdTracking()
This triggers the ATT permission prompt on iOS 14.5+. Make sure you have added the NSUserTrackingUsageDescription key to your Info.plist.
Permissions
dashx-ios SDK offers the following methods to manage notification permissions:
// Request permission to receive notifications
DashX.requestNotificationPermission { status in
// status is a UNAuthorizationStatus value
}
// Check current permission status without prompting
DashX.getNotificationPermissionStatus { status in
// status is a UNAuthorizationStatus value
}
If you manage push tokens manually, you can set them directly:
// Set the APNS device token (typically in didRegisterForRemoteNotificationsWithDeviceToken)
DashX.setAPNSToken(to: deviceToken)
// Set the FCM token (if using Firebase Cloud Messaging)
DashX.setFCMToken(to: fcmToken)
Additionally the SDK tracks network information about the current network connection, containing bluetooth, carrier, cellular, and wifi.
Troubleshooting
As an optional step, you can set the log level for debugging your integration:
DashXLog.setLogLevel(to: .debug)
By default, the log level is set to .error. You can set it to one of: .debug (most logs), .info, .error or .off (no logs).
Usage
Most public methods have async/await variants for Swift concurrency. For example, try await DashX.identify(options:), try await DashX.subscribe(), try await DashX.fetchRecord(urn:), etc.
let prefs = try await DashX.fetchStoredPreferences()
let record = try await DashX.fetchRecord(urn: "blog/abc123")
let records = try await DashX.searchRecords(resource: "blog", limit: 10)
try await DashX.identify(options: ["email": "user@example.com"])
User Management
setIdentity)You can set uid without token and still use public features/resources (for example identify, track, and public content). The token is the DashX Identity Token: a JWT from your backend, signed with your workspace private key (see User management). Setting token grants access to your private resources in DashX—use it only for users who need that access, and treat it as sensitive. Pass nil for token when that access is not needed. uid and token are both optional (String?).
// User id only — public features/resources (add token for your private resources)
DashX.setIdentity(uid: "123", token: nil)
// Signed-in user + JWT for private resources
DashX.setIdentity(uid: "123", token: "your-identity-token")
// After token refresh, persist the new token (same uid)
DashX.setIdentity(uid: "123", token: "refreshed-identity-token")
// Send user attributes (Result-based or async)
DashX.identify(options: [
"uid": "123",
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com"
]) { result in
switch result {
case .success:
break
case .failure(let error):
print(error.localizedDescription)
}
}
// Or with async/await:
// try await DashX.identify(options: ["email": "user@example.com"])
// Clear identity, generate new anonymous ID, and unsubscribe from push
DashX.reset()
Analytics
DashX.track("Button Clicked", withData: [
"label": "Click here",
"placement": "top"
])
Track screen views:
DashX.screen("HomeScreen", withData: [
"referrer": "deep_link"
])
Messaging
Push notifications require your app to be configured for Firebase Cloud Messaging or APNs.
If you use DashXAppDelegate (from the DashXFirebase module), notification delivered, clicked, and dismissed events are tracked automatically. No manual trackMessage() calls are needed.
// Subscribe for Push Notifications
// Important: identify must complete before calling subscribe,
// so the server knows which user to associate the device token with.
// Using async/await (recommended):
Task {
try await DashX.identify(options: ["uid": "user-123"])
try await DashX.subscribe()
}
// Using callbacks:
DashX.identify(options: ["uid": "user-123"]) { result in
if case .success = result {
DashX.subscribe()
}
}
// Unsubscribe from Push Notifications
DashX.unsubscribe { result in
if case .failure(let error) = result { print(error) }
}
// Or: try await DashX.unsubscribe()
// Manage User Preferences (requires an identified user — setIdentity with uid + JWT when private resources require it)
DashX.fetchStoredPreferences { result in
switch result {
case .success(let preferences):
print(preferences) // [String: Any?]
case .failure(let error):
print(error.localizedDescription)
}
}
DashX.saveStoredPreferences(preferenceData: ["push": true, "email": false]) { result in
switch result {
case .success(let saved):
print(saved)
case .failure(let error):
print(error.localizedDescription)
}
}
CMS
DashX.fetchRecord(
urn: "email/welcome",
language: "en_US",
preview: true
) { result in
switch result {
case .success(let record):
print(record)
case .failure(let error):
print(error)
}
}
fetchRecord accepts the following optional arguments:
| Name | Type | Example |
|---|---|---|
preview | Bool | true |
language | String | "en_US" |
fields | [[String: Any]] | |
include | [[String: Any]] | |
exclude | [[String: Any]] |
DashX.searchRecords(
resource: "email",
language: "en_US",
order: [["created_at": "DESC"]],
limit: 10,
preview: true
) { result in
switch result {
case .success(let records):
print(records)
case .failure(let error):
print(error)
}
}
searchRecords accepts the following optional arguments:
| Name | Type | Example |
|---|---|---|
filter | [String: Any] | ["status": "published"] |
order | [[String: Any]] | [["created_at": "DESC"]] |
limit | Int | 10 |
page | Int | 1 |
preview | Bool | true |
language | String | "en_US" |
fields | [[String: Any]] | |
include | [[String: Any]] | |
exclude | [[String: Any]] |
Assets
DashX.uploadAsset(
fileURL: localFileURL,
resource: "users",
attribute: "avatar"
) { result in
switch result {
case .success(let asset):
print(asset)
case .failure(let error):
print(error)
}
}
Fetch an asset's status and URL:
DashX.fetchAsset(assetId: "asset-id") { result in
switch result {
case .success(let asset):
print(asset.url)
case .failure(let error):
print(error)
}
}
Asset uploads poll for completion with exponential backoff (2 s base, capped at 60 s). You can configure DashXClient.maxAssetPollRetries (default: 5) and DashXClient.assetPollBaseInterval (default: 2.0 s).
Error Handling
Completion-based methods return Result types. The SDK defines the following error types via DashXClientError:
| Error | Retryable | When |
|---|---|---|
noArgsInIdentify | No | identify() called without options |
notIdentified | No | Operation requires an identified user |
graphQLErrors | No | Server returned GraphQL errors |
networkError | Yes | Network-level failure |
assetIsNotReady | Yes | Asset still being processed |
assetIsNotUploaded | No | Asset upload failed |
customError | No | Other SDK error |
Use isRetryable to decide whether to retry:
DashX.fetchRecord(urn: "blog/abc") { result in
if case .failure(let error) = result,
let dashXError = error as? DashXClientError,
dashXError.isRetryable {
// retry later
}
}
All SDK errors are represented by DashXClientError, which conforms to LocalizedError. Each error provides errorDescription and recoverySuggestion for user-facing messages:
DashX.fetchStoredPreferences { result in
switch result {
case .success(let prefs):
print(prefs)
case .failure(let error):
print(error.localizedDescription)
if let dashxError = error as? DashXClientError {
print(dashxError.recoverySuggestion ?? "")
if dashxError.isRetryable {
// retry later
}
}
}
}
Offline Event Queue
Failed track() calls are automatically queued and retried with exponential backoff (2 s base, capped at 5 min, up to 10 retries, with jitter). The queue holds up to 1,000 events, is persisted via UserDefaults, and flushes automatically after configure() or when the network becomes available.
To manually flush:
DashX.flushEventQueue()
The sections above mirror the dashx-ios README (installation, offline queue, errors, asset polling, and async/await). Prefer this page for narrative context; the repo README stays focused on quick copy-paste snippets.