Iterable Swift SDK: Architecture Documentation
This issue documents a comprehensive analysis of the error reporting, logging, and offline mode systems in the Iterable Swift SDK. This serves as developer reference documentation.
Table of Contents
- Error Reporting & Logging System
- Offline Mode & Task Persistence
1. Error Reporting & Logging System
1.1 Logging Architecture
Log Levels
The SDK defines three log levels in Constants.swift:492:
@objc(IterableLogLevel) public enum LogLevel: Int {
case debug = 1
case info
case error
}
Global Logging Functions
Three convenience functions in IterableLogging.swift:
| Function |
Level |
Emoji Marker |
ITBDebug() |
.debug |
💚 |
ITBInfo() |
.info |
💛 |
ITBError() |
.error |
❤️ |
Each captures file, method, line number, and thread pointer automatically.
Log Message Format
Generated by IterableLogUtil.formatLogMessage:
HH:mm:ss.SSSS:0x7ff8c9c0e:FileName:methodName:123: Your message here
Built-in Log Delegates
| Delegate |
Behavior |
DefaultLogDelegate |
Uses os_log for messages >= minLogLevel (default: .info) |
AllLogDelegate |
Prints everything to console via print() |
NoneLogDelegate |
Silent - no output |
Custom Log Delegate Protocol
@objc public protocol IterableLogDelegate: AnyObject {
@objc(log:message:)
func log(level: LogLevel, message: String)
}
1.2 Error Type Hierarchy
| Error Type |
Location |
Purpose |
IterableError |
Pending.swift:7 |
Generic SDK errors |
SendRequestError |
RequestSender.swift:9 |
Network request failures |
NetworkError |
NetworkHelper.swift:7 |
Low-level network errors |
IterableTaskError |
IterableTaskError.swift:7 |
Offline task queue errors |
SendRequestError Structure
public struct SendRequestError: Error {
public let reason: String? // Human-readable description
public let data: Data? // Raw response data
public let httpStatusCode: Int? // HTTP status code
public let iterableCode: String? // Iterable-specific error code (401 responses)
public let originalError: Error? // Underlying error
}
Authentication Error Types
AuthFailure:
@objcMembers public class AuthFailure: NSObject {
public let userKey: String?
public let failedAuthToken: String?
public let failedRequestTime: Int
public let failureReason: AuthFailureReason
}
AuthFailureReason enum:
authTokenExpired
authTokenGenericError
authTokenExpirationInvalid
authTokenSignatureInvalid
authTokenFormatInvalid
authTokenInvalidated
authTokenPayloadInvalid
authTokenUserKeyInvalid
authTokenNull
authTokenGenerationError
authTokenMissing
1.3 Asynchronous Error Propagation
The SDK uses a custom Pending<Value, Failure> Promise implementation:
public class Pending<Value, Failure> where Failure: Error {
func onSuccess(block: @escaping ((Value) -> Void)) -> Pending<Value, Failure>
func onError(block: @escaping ((Failure) -> Void)) -> Pending<Value, Failure>
func map<NewValue>(_ closure: @escaping (Value) -> NewValue) -> Pending<NewValue, Failure>
func flatMap<NewValue>(_ closure: @escaping (Value) -> Pending<NewValue, Failure>) -> Pending<NewValue, Failure>
func mapFailure<NewFailure>(_ closure: @escaping (Failure) -> NewFailure) -> Pending<Value, NewFailure>
}
1.4 Retry Mechanisms
Network Retry (NetworkHelper.swift)
- Max retries: 5
- Retry delay: 2 seconds (after first 2 immediate retries)
- Only retries on 5xx server errors
- 4xx errors fail immediately
JWT Auth Token Retry (RetryPolicy)
public class RetryPolicy {
var maxRetry: Int // Default: 10
var retryInterval: Double // Default: 6 seconds
var retryBackoff: BackoffType // .linear or .exponential
}
1.5 Health Monitoring & Circuit Breaker
HealthMonitor implements a circuit breaker pattern:
- Tracks database errors
- When tripped,
canSchedule() and canProcess() return false
RequestHandler falls back to online-only mode
- Max tasks limit: 1000
1.6 Notification-Based Error Events
static let iterableTaskScheduled = Notification.Name("itbl_task_scheduled")
static let iterableTaskFinishedWithSuccess = Notification.Name("itbl_task_finished_with_success")
static let iterableTaskFinishedWithRetry = Notification.Name("itbl_task_finished_with_retry")
static let iterableTaskFinishedWithNoRetry = Notification.Name("itbl_task_finished_with_no_retry")
static let iterableNetworkOffline = Notification.Name("itbl_network_offline")
static let iterableNetworkOnline = Notification.Name("itbl_network_online")
2. Offline Mode & Task Persistence
2.1 Architecture Overview
┌──────────────────────────────────────────────────────────────────┐
│ RequestHandler │
│ chooseRequestProcessor() → checks HealthMonitor.canSchedule() │
└─────────────────┬──────────────────────────┬─────────────────────┘
│ │
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────────────┐
│ OnlineRequestProcessor │ │ OfflineRequestProcessor │
│ (Immediate HTTP) │ │ │
└─────────────────────────┘ │ IterableTaskScheduler │
│ ↓ │
│ CoreData (SQLite) │
│ ↓ │
│ IterableTaskRunner │
│ ↓ │
│ IterableAPICallTaskProcessor │
└─────────────────────────────────┘
2.2 Offline Mode Activation
Controlled by remote configuration from Iterable backend:
private func checkRemoteConfiguration() {
requestHandler.getRemoteConfiguration().onSuccess { remoteConfiguration in
self.localStorage.offlineMode = remoteConfiguration.offlineMode
self.requestHandler.offlineMode = remoteConfiguration.offlineMode
}
}
2.3 CoreData Schema
Entity: IterableTaskManagedObject
| Attribute |
Type |
Notes |
id |
String |
UUID, indexed |
name |
String? |
API path |
type |
String |
Always "apiCall" |
data |
Binary |
JSON-encoded request |
scheduledAt |
Date |
Execution time, indexed |
requestedAt |
Date |
Original request time |
createdAt |
Date |
Record creation |
attempts |
Int64 |
Retry count |
processing |
Bool |
Lock flag |
failed |
Bool |
Permanent failure |
blocking |
Bool |
Blocks queue |
2.4 Task Scheduling Flow
OfflineRequestProcessor.sendIterableRequest() called
- Creates
IterableAPICallRequest with full request data
IterableTaskScheduler.schedule() encodes to JSON and saves to CoreData
- Posts
.iterableTaskScheduled notification
- Returns
Pending<> mapped to taskId
2.5 Task Runner
- Listens for: task scheduled, app foreground, connectivity changes
- Processes queue FIFO by
scheduledAt
- Default interval: 1 minute between checks
- Respects
HealthMonitor.canProcess() guard
2.6 Network Connectivity Detection
Dual-layer approach:
- NWPathMonitor - Passive OS-level network change detection
- Active HTTP probing - Checks apple.com and google.com
Polling intervals:
- Offline: 1 minute
- Online: 10 minutes
2.7 Task Result Handling
enum IterableTaskResult {
case success(detail: TaskSuccessDetail?)
case failureWithRetry(retryAfter: TimeInterval?, detail: TaskFailureDetail?)
case failureWithNoRetry(detail: TaskFailureDetail?)
}
Retry decision:
- Error contains "offline" → retry later
- HTTP 4xx → no retry, delete task
- HTTP 5xx → no retry (already retried by NetworkHelper)
2.8 Supported Offline Operations
✅ Queued offline:
updateCart()
trackPurchase()
trackPushOpen()
track(event:)
trackInAppOpen/Click/Close/Delivery()
track(inboxSession:)
inAppConsume()
- Embedded message events
❌ Always online:
register(token:)
disableDevice()
updateUser()
updateEmail()
updateSubscriptions()
- Remote configuration fetch
- In-app message fetch
2.9 Timing Constants
| Constant |
Value |
| Task runner interval |
1 minute |
| Offline polling |
1 minute |
| Online polling |
10 minutes |
| Connectivity timeout |
5 seconds |
| Max queue size |
1000 tasks |
Future Additions
This issue will be updated with additional deep dives into:
This documentation was created through code analysis of the Iterable Swift SDK.
Iterable Swift SDK: Architecture Documentation
This issue documents a comprehensive analysis of the error reporting, logging, and offline mode systems in the Iterable Swift SDK. This serves as developer reference documentation.
Table of Contents
1. Error Reporting & Logging System
1.1 Logging Architecture
Log Levels
The SDK defines three log levels in
Constants.swift:492:Global Logging Functions
Three convenience functions in
IterableLogging.swift:ITBDebug().debugITBInfo().infoITBError().errorEach captures file, method, line number, and thread pointer automatically.
Log Message Format
Generated by
IterableLogUtil.formatLogMessage:Built-in Log Delegates
DefaultLogDelegateos_logfor messages >= minLogLevel (default:.info)AllLogDelegateprint()NoneLogDelegateCustom Log Delegate Protocol
1.2 Error Type Hierarchy
IterableErrorPending.swift:7SendRequestErrorRequestSender.swift:9NetworkErrorNetworkHelper.swift:7IterableTaskErrorIterableTaskError.swift:7SendRequestError Structure
Authentication Error Types
AuthFailure:
AuthFailureReason enum:
authTokenExpiredauthTokenGenericErrorauthTokenExpirationInvalidauthTokenSignatureInvalidauthTokenFormatInvalidauthTokenInvalidatedauthTokenPayloadInvalidauthTokenUserKeyInvalidauthTokenNullauthTokenGenerationErrorauthTokenMissing1.3 Asynchronous Error Propagation
The SDK uses a custom
Pending<Value, Failure>Promise implementation:1.4 Retry Mechanisms
Network Retry (NetworkHelper.swift)
JWT Auth Token Retry (RetryPolicy)
1.5 Health Monitoring & Circuit Breaker
HealthMonitorimplements a circuit breaker pattern:canSchedule()andcanProcess()returnfalseRequestHandlerfalls back to online-only mode1.6 Notification-Based Error Events
2. Offline Mode & Task Persistence
2.1 Architecture Overview
2.2 Offline Mode Activation
Controlled by remote configuration from Iterable backend:
2.3 CoreData Schema
Entity: IterableTaskManagedObject
idnametypedatascheduledAtrequestedAtcreatedAtattemptsprocessingfailedblocking2.4 Task Scheduling Flow
OfflineRequestProcessor.sendIterableRequest()calledIterableAPICallRequestwith full request dataIterableTaskScheduler.schedule()encodes to JSON and saves to CoreData.iterableTaskSchedulednotificationPending<>mapped to taskId2.5 Task Runner
scheduledAtHealthMonitor.canProcess()guard2.6 Network Connectivity Detection
Dual-layer approach:
Polling intervals:
2.7 Task Result Handling
Retry decision:
2.8 Supported Offline Operations
✅ Queued offline:
updateCart()trackPurchase()trackPushOpen()track(event:)trackInAppOpen/Click/Close/Delivery()track(inboxSession:)inAppConsume()❌ Always online:
register(token:)disableDevice()updateUser()updateEmail()updateSubscriptions()2.9 Timing Constants
Future Additions
This issue will be updated with additional deep dives into:
This documentation was created through code analysis of the Iterable Swift SDK.