Intelligence·Engagement·Defense
Swift SDK for analytics, push notifications via APNs, in-app messages (banner / bottom-sheet / modal), and crash capture — including pure-Swift crashes via signal handler. Matches the Android SDK's two-line integration.
Dijji.initialize(siteKey:)Dijji.trackDijji.setUserPropertyidentify · optIn / optOutfatalError, forced unwrap, OOB) via SIGABRT/SIGTRAP/SIGSEGV/SIGBUS/SIGILL/SIGFPEDijjiPush.registerTokenhandleNotificationpush_received + push_opened events fire automaticallyDijjiNotificationServiceHelper) — image attachment in 5 lines from your NSE target__dijji_message_received / _clicked / _dismissedIn Xcode, File → Add Package Dependencies, paste:
https://github.com/urbaneyed/dijji-ios
Pick the modules you want. DijjiCore is required; DijjiPush and DijjiMessages are optional.
Then in your AppDelegate (or App body for SwiftUI):
import DijjiCore @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ app: UIApplication, didFinishLaunchingWithOptions opts: [UIApplication.LaunchOptionsKey : Any]?) -> Bool { Dijji.initialize(siteKey: "ws_a647ba153d0911f1b7") return true } }
That's it. The SDK auto-captures app_open, app_background, session_start/end, and app_install (once on first launch). Custom events via Dijji.track; user properties via Dijji.setUserProperty; opt-out via Dijji.optOut().
Before any iOS push reaches a device, your Dijji site needs four pieces of Apple credentials. Generate them in the Apple Developer portal, then upload at /app/sites/{site_key}/mobile.
AuthKey_<KeyID>.p8. Save it; you can't re-download.com.kaabilprogram.app). Sent as the apns-topic header on every push.The .p8 contents are encrypted at rest (AES-256-CBC, AES key derived from dijji.cryptoKey in the .env). Decrypted only at push-dispatch time, never exposed via API.
An iOS dev who can't wait for the Swift SDK can integrate against these directly. Same endpoints the Android SDK uses; the only difference is what your client sends as platform.
| Method | Path | Purpose |
|---|---|---|
| POST | /t/app/collect |
Batch event ingestion. JSON body: {site, visitor_id, events: [{name, ts, props}]}. Auto-events the SDK fires: app_open, app_background, screen_view, session_start, session_end, app_install. Custom events go through the same channel. |
| POST | /t/app/install |
Fired once on first ever launch. Body: {site, visitor_id, device_id, platform: "ios", os, model, locale, ...}. Backend persists to dijji_app_installs + creates the user row in dijji_app_users. |
| POST | /t/app/token |
Register an APNs device token. Body: {site, visitor_id, token, platform: "ios"}. Stored in dijji_push_tokens; revoked automatically when APNs returns BadDeviceToken or Unregistered. |
| POST | /t/app/crash |
Crash report ingestion. Body includes the stack, signal/exception type, breadcrumbs, app + device state. Synchronous on the client (3s timeout) so the report doesn't get lost when the process dies. |
| POST | /t/app/session |
Session rollup — upsert keyed on session_id. Sent on UIApplicationDidEnterBackgroundNotification with duration, screen count, event count. |
| GET | /t/app/inbox |
Fetch pending in-app messages. Query: ?site=...&visitor=...&platform=ios. Returns banner / bottom_sheet / modal payloads with personalization tokens already merged. Marks messages delivered on read. |
| GET | /t/rules |
Mobile config: kill switch, rollout %, poll/flush intervals, sample rates. Query: ?platform=ios. Cache for 5 min on the client. |
When a Dijji-dispatched push arrives, the JSON looks like:
{
"aps": {
"alert": { "title": "Welcome back", "body": "We picked 3 jobs for you" },
"sound": "default"
},
"dijji": "1", // flag to claim Dijji-originated pushes
"deep_link": "/jobs",
"trigger_id": "42",
"push_id": "199"
}
In your UNUserNotificationCenterDelegate, claim notifications with userInfo["dijji"] == "1", fire a push_opened event with push_id + trigger_id, and route on deep_link.
For pushes with images on iOS, your app needs a Notification Service Extension. Apple delivers the push to the extension first; the extension downloads the image, attaches it, then hands the result to iOS for display. Without an extension, iOS shows the text-only payload.
Adding one is a 5-line job:
DijjiNotificationService).DijjiPush as a dependency of the new target.NotificationService.swift body with:import UserNotifications import DijjiPush class NotificationService: UNNotificationServiceExtension { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { DijjiNotificationServiceHelper.handle(request, contentHandler: contentHandler) } }
That's it. The helper looks for image_url in the push payload (which the Dijji backend stamps when you fill the "Image URL" field in the push composer or trigger), downloads it with a 25-second cap, infers the file extension from Content-Type, attaches it as UNNotificationAttachment, and hands the rendered content back to iOS. Falls back to text-only on any download failure rather than dropping the push.
BigPictureStyle notification on Android. iOS is the only platform that requires the extension.
identifierForVendor (IDFV) only — per-app, never cross-app. Apple doesn't require ATT for IDFV.dijji.com (HSTS preloaded). APNs is HTTP/2 with mutual TLS to Apple.Dijji.optOut() stops all collection and clears the local queue. Dijji.optIn() resumes.dijji-messages uses only system frameworks.iOS 16.1+ adds a new push-driven UI surface: Live Activities show on the lock screen and the Dynamic Island, updated in real time by the server. Dijji's DijjiLiveActivity module handles the APNs side — you keep ownership of the SwiftUI layout, we deliver the state pushes.
Package.swift.product(name: "DijjiLiveActivity", package: "dijji-ios")
ActivityAttributes in your app targetimport ActivityKit
struct OrderTrackingAttributes: ActivityAttributes {
public struct ContentState: Codable, Hashable {
var status: String // "Confirmed" | "Out for delivery" | "Delivered"
var etaMinutes: Int
}
var orderId: Int
var restaurantName: String
}
import ActivityKit
import DijjiLiveActivity
let attrs = OrderTrackingAttributes(orderId: 42, restaurantName: "Baang")
let initial = OrderTrackingAttributes.ContentState(
status: "Confirmed", etaMinutes: 35
)
if let activity = try? Activity<OrderTrackingAttributes>.request(
attributes: attrs,
contentState: initial,
pushType: .token
) {
// ONE LINE — auto-registers every push token Apple rotates to.
DijjiLiveActivity.observe(activity, activityId: "order_42")
}
// When the activity ends:
DijjiLiveActivity.end(activityId: "order_42")
Standard SwiftUI ActivityConfiguration — lock screen + Dynamic Island compact / minimal / expanded. Apple's HIG covers the layout patterns.
From /live, pick the user's mobile card → pick "Live Activity update" → type the new content_state JSON. The server signs an APNs liveactivity push and Apple updates the layout on-device within seconds.
From a trigger or journey, queue an action_type=live_activity_update push with action_config = {activity_id, content_state, alert?} — same dispatch path, same dashboard.
What's shipped, what's next:
| Version | Scope | Status |
|---|---|---|
| Backend | APNs dispatcher, schema, push routing, uninstall sweep, settings UI, push performance dashboard | Live |
| SDK v1.0-alpha | Two-line init, lifecycle auto-capture, custom events, user properties, NSException crash chain, push token registration, deep links | Live |
| SDK v1.1-alpha | UIKit in-app message renderer (banner / sheet / modal) with theme palette, drag-to-dismiss, queue-and-present | Live |
| SDK v1.2-alpha | POSIX signal crash handler — catches pure-Swift crashes (fatalError, forced unwrap nil, OOB) via SIGABRT/SIGTRAP/SIGSEGV/SIGBUS/SIGILL/SIGFPE | Live |
| SDK v1.3-alpha | 4 new in-app formats: in_app_hero (full-bleed, dual CTA), in_app_nps (0–10 score), in_app_reactions (emoji), in_app_countdown (live ticker). image_url support across banner/sheet/modal. | Live |
| SDK v1.4-alpha | Live Activities (iOS 16.1+) — new DijjiLiveActivity module bridges your Activity<Attrs>.pushTokenUpdates to Dijji's APNs dispatcher. Lock screen + Dynamic Island layouts driven by server-pushed content state. Order tracking, booking countdowns, score tickers — all without your own push infra. | Live |
| v1.5 | Mach exception port handler · server-side dSYM symbolication | Next |
| v1.5+ | Carousel push · quick-reply · action buttons · time-sensitive notifications · SwiftUI-native message components | Planned |