Swift SDK Reference¶
Complete API reference for the MailJawn Swift SDK. For a quick-start walkthrough, see Install the Swift SDK.
Requirements¶
| Requirement | Minimum |
|---|---|
| iOS | 15.0+ |
| macOS | 12.0+ |
| Swift | 6 |
| Xcode | with Swift Package Manager |
Installation¶
Add the SDK using Swift Package Manager:
- In Xcode, go to File > Add Package Dependencies...
-
Enter the package URL:
-
Select your version rule and add the package to your target.
Then import it:
Configuration¶
configure(apiKey:projectId:baseURL:)¶
Initialize the SDK. Call this once, as early as possible in your app's lifecycle.
try await Mailjawn.configure(
apiKey: "mj_your_api_key_here",
projectId: UUID(uuidString: "your-project-uuid")!
)
| Parameter | Type | Required | Description |
|---|---|---|---|
apiKey |
String |
Yes | Your API key. Must start with mj_. |
projectId |
UUID |
Yes | Your app's project UUID from the MailJawn dashboard. |
baseURL |
URL? |
No | Override the default API URL. Defaults to https://api.mailjawn.com/api/v1. |
Behavior:
- Validates the API key starts with
mj_— throwsMailjawnError.invalidAPIKeyif not - Stores the API key securely in the device Keychain
- Must be called before any other SDK method
Warning
Calling any SDK method before configure() throws MailjawnError.notConfigured.
Example — SwiftUI App init:
import MailjawnSwift
@main
struct MyApp: App {
init() {
Task {
try await Mailjawn.configure(
apiKey: "mj_your_api_key_here",
projectId: UUID(uuidString: "your-project-uuid")!
)
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
User Identification¶
identify(userId:)¶
Associate the current device with a user ID. This is a local operation — no network call is made.
| Parameter | Type | Required | Description |
|---|---|---|---|
userId |
String |
Yes | Your app's user identifier. |
Call this when a user signs in. The user ID is attached to subsequent API calls.
setEmail(_:) and setEmail(_:name:)¶
Register or update a subscriber in MailJawn. This is the primary method for getting users into your subscriber list.
// Email only
let result = try await Mailjawn.setEmail("user@example.com")
// Email with name
let result = try await Mailjawn.setEmail("user@example.com", name: "Jane Doe")
| Parameter | Type | Required | Description |
|---|---|---|---|
email |
String |
Yes | Subscriber's email address. |
name |
String |
No | Subscriber's display name. |
Returns: IdentifyResult
public struct IdentifyResult: Sendable {
public let subscriberId: UUID // The subscriber's unique ID
public let isNewSubscriber: Bool // true if just created, false if updated
}
Behavior:
- Makes a network call to
POST /subscribers/identify/ - Upserts the subscriber — creates if new, updates if existing
- Automatically attaches device metadata to the request
- Validates email client-side (must contain
@with non-empty local and domain parts, domain must contain.) - Throws
MailjawnError.invalidEmailif validation fails - Triggers welcome automations for new subscribers
setUserProperties(_:)¶
Attach custom fields to the current subscriber. These fields appear in the subscriber profile and can be used in segments and template variables.
try await Mailjawn.setUserProperties([
"plan": "premium",
"signup_source": "referral",
"trial_days_left": 14,
"is_beta": true
])
| Parameter | Type | Required | Description |
|---|---|---|---|
properties |
[String: Any] |
Yes | Key-value pairs to merge into the subscriber's custom fields. |
Accepted value types:
| Swift Type | JSON Type | Example |
|---|---|---|
String |
string | "premium" |
Int |
number | 42 |
Double |
number | 3.14 |
Bool |
boolean | true |
Properties are merged with existing custom fields — keys you don't include are left unchanged.
reset()¶
Clear all local SDK state. Call this when a user logs out.
Behavior:
- Clears the locally stored user ID, email, and custom properties
- No network call is made
- The subscriber record in MailJawn is unaffected
- You'll need to call
identify()andsetEmail()again for the next user
Auto-Captured Fields¶
When you call setEmail(), the SDK automatically collects and sends device metadata alongside the subscriber data. You don't need to set these manually.
| Field | Source | Example |
|---|---|---|
deviceType |
UIDevice / Host |
"iPhone", "iPad", "Mac" |
osVersion |
ProcessInfo / UIDevice |
"iOS 17.2", "macOS 14.1" |
appVersion |
Bundle.main |
"2.3.1" (from CFBundleShortVersionString) |
locale |
Locale.current |
"en_US", "it_IT" |
timezone |
TimeZone.current |
"America/New_York" (IANA identifier) |
sdkVersion |
SDK constant | "1.0.0" |
These fields appear on the subscriber profile in the dashboard and can be used for segments (e.g., "all iPhone users on iOS 17+").
Error Handling¶
All throwing SDK methods can throw MailjawnError:
public enum MailjawnError: Error, Sendable {
case notConfigured
case invalidAPIKey
case invalidEmail
case networkError(underlying: any Error)
case serverError(statusCode: Int, message: String)
case decodingError(underlying: any Error)
case encodingError(underlying: any Error)
case keychainError(status: OSStatus)
}
| Error | When It Happens |
|---|---|
notConfigured |
You called an SDK method before configure(). |
invalidAPIKey |
The API key doesn't start with mj_. |
invalidEmail |
The email failed client-side validation. |
networkError |
The device couldn't reach the server (no internet, DNS failure, timeout). |
serverError |
The server returned a 4xx or 5xx response. Check statusCode and message. |
decodingError |
The server response couldn't be parsed. |
encodingError |
The request body couldn't be serialized (e.g., invalid custom field types). |
keychainError |
The API key couldn't be stored in or read from the Keychain. |
Example — handling errors:
do {
let result = try await Mailjawn.setEmail("user@example.com")
print("Subscriber \(result.subscriberId), new: \(result.isNewSubscriber)")
} catch let error as MailjawnError {
switch error {
case .notConfigured:
print("SDK not configured — call Mailjawn.configure() first")
case .invalidEmail:
print("Invalid email address")
case .networkError(let underlying):
print("Network issue: \(underlying.localizedDescription)")
case .serverError(let code, let message):
print("Server error \(code): \(message)")
default:
print("Error: \(error.localizedDescription)")
}
}
Thread Safety¶
The SDK is fully thread-safe. All public methods are async and use a Swift actor internally to manage state. All public types conform to Sendable. You can call SDK methods from any thread or task without synchronization.
Keychain Storage¶
The SDK stores your API key in the device Keychain with kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly protection. This means:
- The key is encrypted at rest
- It's available after the first device unlock (no need to unlock before background operations)
- It doesn't sync to other devices or iCloud Keychain
- It persists across app updates but is removed on app uninstall
Planned Features¶
Note
The following methods are specified in the SDK design but not yet shipped. They will be added in a future release.
track(event:properties:)— Record custom events for automation triggersflush()— Force-send any queued events immediately
See also: Quick Start > Install the Swift SDK | REST API for platform-agnostic access