Swift SDK -- Getting Started
Swift SDK -- Getting Started
Install the Modula Swift SDK and make your first async/await API calls from any Apple platform.
Platform Requirements
| Requirement | Minimum |
|---|---|
| iOS | 16.0+ |
| macOS | 13.0+ |
| tvOS | 16.0+ |
| watchOS | 9.0+ |
| Swift | 5.9+ |
| Xcode | 15.0+ |
Good to know: The SDK uses Swift concurrency (
async/await),Sendableconformance throughout, andURLSessionfor networking. Zero third-party dependencies.
Installation
Add the package to your Package.swift:
dependencies: [
.package(url: "https://github.com/hegner123/modulacms.git", from: "0.1.0"),
]
Then add the Modula target to your dependency list:
.target(
name: "YourApp",
dependencies: [
.product(name: "Modula", package: "modulacms"),
]
)
In Xcode: File > Add Package Dependencies, paste the repository URL, and select the Modula library.
Creating a Client
All API access goes through ModulaClient. Initialize it with a ClientConfig:
import Modula
let client = try ModulaClient(config: ClientConfig(
baseURL: "https://cms.example.com",
apiKey: "your-api-token"
))
ClientConfig fields:
| Field | Type | Required | Description |
|---|---|---|---|
baseURL |
String |
Yes | CMS server URL. Trailing slashes are stripped automatically. |
apiKey |
String |
No | Bearer token for API authentication. Defaults to empty string. |
urlSession |
URLSession? |
No | Custom URL session. Defaults to a session with 30s request timeout, 300s resource timeout, and cookies disabled. |
ModulaClient(config:) throws if baseURL is empty.
Custom URLSession
Pass your own URLSession for proxy configuration, certificate pinning, or custom caching:
let sessionConfig = URLSessionConfiguration.default
sessionConfig.timeoutIntervalForRequest = 10
sessionConfig.waitsForConnectivity = true
let client = try ModulaClient(config: ClientConfig(
baseURL: "https://cms.example.com",
apiKey: "your-api-token",
urlSession: URLSession(configuration: sessionConfig)
))
First Requests
All SDK methods are async throws. Use them inside async contexts.
List Datatypes
let datatypes = try await client.datatypes.list()
for dt in datatypes {
print("\(dt.name): \(dt.label)")
}
Get a Single Datatype
let id = DatatypeID("01HXYZ...")
let datatype = try await client.datatypes.get(id: id)
print(datatype.label)
Authenticate and Get Current User
let loginResponse = try await client.auth.login(params: LoginParams(
email: "admin@example.com",
password: "your-password"
))
print("Token: \(loginResponse.token)")
let me = try await client.auth.me()
print("Logged in as: \(me.email)")
Fetch Content by Slug
let pageData = try await client.content.getPage(slug: "blog/hello-world")
// pageData is raw Data -- decode as needed for your frontend
Query Content by Datatype
let result = try await client.query.query(
datatype: "blog_post",
params: QueryParams(sort: "-date_created", limit: 10, status: "published")
)
print("Found \(result.total) posts")
for item in result.data {
print(item.fields["title"] ?? "untitled")
}
Async/Await Patterns
Every method on ModulaClient is async throws. Standard Swift concurrency patterns apply.
Sequential Requests
let datatypes = try await client.datatypes.list()
let fields = try await client.fields.list()
Concurrent Requests
Use async let for independent requests:
async let datatypes = client.datatypes.list()
async let users = client.users.list()
async let routes = client.routes.list()
let (dt, u, r) = try await (datatypes, users, routes)
TaskGroup for Dynamic Concurrency
let ids: [ContentID] = [...]
let results = try await withThrowingTaskGroup(of: ContentData.self) { group in
for id in ids {
group.addTask {
try await client.contentData.get(id: id)
}
}
var items: [ContentData] = []
for try await item in group {
items.append(item)
}
return items
}
Cancellation
All SDK methods respect Swift's cooperative cancellation. Cancelling a parent Task automatically cancels in-flight URLSession requests.
let task = Task {
let data = try await client.datatypes.list()
// ...
}
// Later:
task.cancel()
Sendable Safety
ModulaClient and all resource types conform to Sendable. Share the client across actors and tasks without additional synchronization.
actor ContentManager {
let client: ModulaClient
init(client: ModulaClient) {
self.client = client
}
func fetchDatatypes() async throws -> [Datatype] {
try await client.datatypes.list()
}
}
Next Steps
- Content Operations -- CRUD, content delivery, trees, media upload, query API
- Error Handling -- APIError, do/catch patterns, network vs API errors
- Reference -- all resources, ID types, enums, JSONValue