Architecture Overview
High-Level Diagram
Application Code ──► Public API (EngagementCloud.* namespaces)
│
▼
Domain Modules (Contact, Event, Push, InApp, DeepLink, Setup)
│
▼
Orchestration / State (SdkContext + ReRegistration States)
│
▼
Networking Layer (Ktor clients per platform)
│
▼
Database (SQLDelight + Key/Value Store)
│
▼
Platform Adapters (Android Services, iOS Delegates, JS SW)
Core Concepts
- Initialization vs. Enabling: Initialization wires dependencies and sets up the database and coroutines. Enabling (
enable) starts tracking, event processing, and network communication. - Central Context (
SdkContext): Stores mutable runtime data (applicationCode, contact identifiers, push token, language) across launches. - Event Bus: Internal events are emitted through
EngagementCloud.eventsasEngagementCloudEventinstances (app events, badge count). On Web, use thetypeproperty to filter events. - Re-registration State Machine: Manages contact linking and push token persistence across app restarts and SDK updates. Maintains consistency even during temporary network failures.
- Multiplatform Abstraction: All business logic lives in
commonMain. Dedicated adapters handle platform-specific operations such as Android Startup, iOS UserNotificationCenter, and JS service worker registration.
Threading & Concurrency
- All asynchronous work uses Kotlin coroutines. Public suspend functions run off the UI thread.
- On Swift and JS, suspend functions are wrapped as async/await-compatible calls.
- The SDK serializes operations that mutate shared context (for example, linking a contact) to avoid race conditions.
Error Handling Philosophy
- Public asynchronous APIs return
Result<Unit>(Kotlin) or throw/promise reject (Swift/JS) on failure. - Non-fatal conditions (already enabled, duplicate link) return domain-specific exceptions inside the result. These idempotent errors are safe to ignore.
Event Flow
- User calls public API (e.g.,
EngagementCloud.event.track(CustomEvent)) - The SDK validates and maps the event to an internal model.
- The event is stored locally (SQLDelight) for reliability.
- A dispatch job schedules the network request.
- On success, the local entry is removed. On failure, the SDK retries with backoff.
- State changes such as badge count updates also emit events.
Database Strategy
- Lightweight key-value for context fields and SQLDelight for queued events.
- All writing operations are persisted immediately for crash resilience.
- A migration layer handles backward-compatible schema updates.
SDK Lifecycle States
| State | Description | Transitions |
|---|---|---|
| Uninitialized | Classes loaded, context not yet available. | app start -> Initialized |
| Initialized | Dependency graph ready; no tracking/network. | enable() -> Enabled |
| Enabled | Full feature set active (events, push token registration). | disable() -> Initialized |
Performance Considerations
- Batching: Multiple events are batched by scheduler to reduce network traffic under high activity.
- Memory: Event objects are short-lived. The database avoids large in-memory queues.
- Push Token: Only sent when changed (deduped).
Security Notes
- All network calls rely on transport layer security. Image URLs for rich push must be HTTPS.
- Cryptography libraries provide secure randomness and hashing.
When to Call What
| Operation | Recommended Timing |
|---|---|
| initialize() (iOS only) | App launch before UI shows |
| enable(config) | After consent and applicationCode are available |
| contact.link(...) | After user authentication and identification |
| push.registerToken(token) | Immediately after system issues token |
| event.track(...) | Any time after enabling |
| inapp.pause()/resume() | Around sensitive flows (checkout, payment) |