Swift SDK (server-side)
Every call in this guide authenticates with your secret API key
(zy_live_* / zy_test_*). A secret key embedded in an app bundle is
extractable from any IPA by anyone who downloads it, and it can send on your
account, read your subscribers, and manage your configuration.
Use this SDK from server-side Swift, or from the backend your iOS app talks to. Your app calls your backend; your backend calls Zyphr. The snippets below assume that shape.
For the end-user auth flows that are safe to call from an app, use the
client-safe ZyphrClient with a publishable
application key (za_pub_*) instead. It ships in @zyphr-dev/node-sdk and
covers browser and React Native today — there is no publishable-key Swift
surface, so a native iOS app needs a backend proxy.
The official Zyphr Swift SDK for server-side Swift and macOS, built with URLSession and Swift async/await. Auto-generated from the OpenAPI specification.
Installation
The Swift SDK is currently distributed by request — contact support@zyphr.dev for access. Public SPM and CocoaPods distribution is on the roadmap.
Configuration
Read the key from the environment on your server. Never inline it in source, and never let it reach an app bundle.
import ZyphrSDK
let config = Configuration(
apiKey: ProcessInfo.processInfo.environment["ZYPHR_API_KEY"]!,
basePath: "https://api.zyphr.dev/v1" // Optional custom endpoint
)
API Classes
The SDK exposes one API class per resource:
| Class | Description |
|---|---|
EmailsAPI | Send and manage email messages |
PushAPI | Send push notifications |
SmsAPI | Send SMS text messages |
InboxAPI | In-app notification inbox |
SubscribersAPI | Manage subscriber profiles |
DevicesAPI | Register push notification devices |
TemplatesAPI | Create and manage templates |
TopicsAPI | Pub/sub topics |
WebhooksAPI | Configure webhook endpoints |
Emails
Send Email
let emails = EmailsAPI(configuration: config)
let result = try await emails.sendEmail(
sendEmailRequest: SendEmailRequest(
to: "user@example.com",
from: "hello@yourapp.com",
subject: "Welcome!",
html: "<h1>Hello!</h1><p>Thanks for signing up.</p>",
text: "Hello! Thanks for signing up.",
replyTo: "support@yourapp.com",
tags: ["welcome", "onboarding"],
metadata: ["userId": "user_123"]
)
)
print("Email sent: \(result.id)")
Send with Template
let result = try await emails.sendEmail(
sendEmailRequest: SendEmailRequest(
to: "user@example.com",
templateId: "welcome-email",
templateData: [
"name": "John",
"actionUrl": "https://yourapp.com/activate",
]
)
)
Get Email Status
let email = try await emails.getEmail(id: "msg_abc123")
print("Status: \(email.status)")
List Emails
let response = try await emails.listEmails(page: 1, perPage: 25)
for email in response.data {
print("\(email.id): \(email.status)")
}
Push Notifications
Send Push
let push = PushAPI(configuration: config)
try await push.sendPush(
sendPushRequest: SendPushRequest(
userId: "user_123",
title: "New Message",
body: "You have a new message",
data: ["messageId": "msg_456"]
)
)
Send to Specific Device
try await push.sendPush(
sendPushRequest: SendPushRequest(
deviceId: "device_abc",
title: "New Message",
body: "You have a new message"
)
)
Silent Push
try await push.sendPush(
sendPushRequest: SendPushRequest(
userId: "user_123",
contentAvailable: true,
data: ["type": "sync", "resource": "messages"]
)
)
Rich Notifications
try await push.sendPush(
sendPushRequest: SendPushRequest(
userId: "user_123",
title: "Photo shared",
body: "Jane shared a photo with you",
imageUrl: "https://yourapp.com/photo.jpg",
badge: 3,
sound: "default",
actionButtons: [
ActionButton(id: "view", title: "View"),
ActionButton(id: "dismiss", title: "Dismiss"),
]
)
)
SMS
let sms = SmsAPI(configuration: config)
let result = try await sms.sendSms(
sendSmsRequest: SendSmsRequest(
to: "+14155551234",
body: "Your verification code is 123456"
)
)
print("SMS sent: \(result.id)")
In-App Inbox
let inbox = InboxAPI(configuration: config)
try await inbox.sendInboxNotification(
sendInboxRequest: SendInboxRequest(
subscriberId: "user_123",
title: "New Comment",
body: "John commented on your post",
actionUrl: "/posts/123#comments"
)
)
Subscribers
Create Subscriber
let subscribers = SubscribersAPI(configuration: config)
let subscriber = try await subscribers.createSubscriber(
createSubscriberRequest: CreateSubscriberRequest(
externalId: "user_123",
email: "user@example.com",
phone: "+14155551234",
name: "John Doe",
metadata: ["plan": "pro"]
)
)
Get Subscriber
let subscriber = try await subscribers.getSubscriber(id: "user_123")
print("Name: \(subscriber.name ?? "unknown")")
Update Subscriber
try await subscribers.updateSubscriber(
id: "user_123",
updateSubscriberRequest: UpdateSubscriberRequest(
name: "Jane Doe",
metadata: ["plan": "enterprise"]
)
)
Delete Subscriber
try await subscribers.deleteSubscriber(id: "user_123")
Device Management
The device API authenticates with your secret key, which must never ship in
an iOS bundle. Post the APNs token to your own backend and register from there,
deriving userId from the authenticated session. See
Device Management and the
iOS push guide.
The snippets below show the request shape for reference — run them from your server, not the app.
Register Device
let devices = DevicesAPI(configuration: config)
try await devices.registerDevice(
registerDeviceRequest: RegisterDeviceRequest(
userId: "user_123",
platform: .ios,
token: apnsToken,
metadata: ["appVersion": "2.1.0"]
)
)
List Devices
let response = try await devices.listDevices(userId: "user_123")
for device in response.data {
print("\(device.platform): \(device.lastActiveAt)")
}
Unregister Device
try await devices.deleteDevice(id: "device_abc")
Topics
Subscribe to Topic
let topics = TopicsAPI(configuration: config)
try await topics.subscribe(
topicId: "promotions",
subscriberId: "user_123"
)
Unsubscribe from Topic
try await topics.unsubscribe(
topicId: "promotions",
subscriberId: "user_123"
)
Send to Topic
try await push.sendToTopic(
topicId: "promotions",
sendPushRequest: SendPushRequest(
title: "Flash Sale!",
body: "50% off everything today"
)
)
Error Handling
All API methods are async and throw errors on failure:
do {
try await emails.sendEmail(request)
} catch let error as ErrorResponse {
switch error {
case .error(let statusCode, let data, _, _):
let message = String(data: data ?? Data(), encoding: .utf8) ?? "Unknown error"
print("API Error (\(statusCode)): \(message)")
switch statusCode {
case 400: print("Bad request — check your parameters")
case 401: print("Unauthorized — check your API key")
case 404: print("Resource not found")
case 429: print("Rate limited — slow down")
default: print("Server error")
}
}
} catch {
print("Network error: \(error.localizedDescription)")
}
Retry with Exponential Backoff
func withRetry<T>(maxAttempts: Int = 3, operation: () async throws -> T) async throws -> T {
var lastError: Error?
for attempt in 0..<maxAttempts {
do {
return try await operation()
} catch let error as ErrorResponse {
if case .error(let statusCode, _, _, _) = error, statusCode == 429 || statusCode >= 500 {
lastError = error
let delay = pow(2.0, Double(attempt))
try await Task.sleep(nanoseconds: UInt64(delay * 1_000_000_000))
continue
}
throw error // Don't retry 4xx errors (except 429)
}
}
throw lastError!
}
// Usage
let result = try await withRetry {
try await emails.sendEmail(request)
}
Thread Safety
All API classes are safe to use from any actor or thread. Each API call creates an independent URLSession task.
For SwiftUI, you can call SDK methods directly from Task blocks:
struct SendButton: View {
var body: some View {
Button("Send") {
Task {
try await emails.sendEmail(request)
}
}
}
}
Requirements
- Swift 5.5+ on macOS 12.0+ or a Linux server runtime
- Xcode 14.0+ (for macOS builds)
The SDK also builds for iOS 15.0+, but see the warning at the top of this page — an iOS target must not carry a secret key.
Next Steps
- Client-side Auth —
ZyphrClientand the publishable key, the credential that is safe in a client app - Push Notifications for iOS — Complete native iOS push integration guide, including the backend-proxy shape
- Push Notifications API — Full push API reference
- Device Management — Managing registered devices