Swift SDK
The official Swift client for the FoPost API, for iOS, macOS, tvOS, and watchOS.
FoPost is the official Swift package. It needs Swift 5.9 or newer and iOS 15, macOS 12, tvOS 15, or watchOS 8, and it has no third-party dependencies: URLSession and Codable only, so nothing lands in your dependency graph but this.
Add it to Package.swift:
dependencies: [
.package(url: "https://github.com/fopost/fopost-swift.git", from: "0.1.0")
]and the product to your target:
.target(name: "YourApp", dependencies: [.product(name: "FoPost", package: "fopost-swift")])In Xcode: File → Add Package Dependencies… and paste the repository URL.
This is a 0.x release. The surface is still settling and a minor version may break something. Pin an exact version if that matters to you.
Source and issues: github.com/fopost/fopost-swift. MIT licensed.
Quick start
import FoPost
let client = try FoPostClient(apiKey: "fp_...") // or omit it and set FOPOST_API_KEY
let workspace = try await client.workspaces.list()[0]
let accounts = try await client.accounts.list(workspaceID: workspace.id)
let post = try await client.posts.create(
CreatePostRequest(
workspaceID: workspace.id,
accounts: accounts.map(\.id),
content: .text("Hello from Swift")))
try await client.posts.publish(post.id)Create a key at Settings → API Keys in the dashboard. It travels as the X-API-Key header, never as a bearer token.
Content
.text builds a single-block post and .thread builds one block per entry. Media is attached per block:
try await client.posts.create(
CreatePostRequest(
workspaceID: workspace.id,
accounts: accounts.map(\.id),
content: [
ContentBlock(text: "First post in the thread"),
ContentBlock(
text: "Second one, with an image",
media: [MediaItem(type: "image", name: "chart.png", url: "https://.../chart.png")]),
]))Uploading through the media library gives you an item that drops straight in:
let uploaded = try await client.media.upload(
workspaceID: workspace.id,
file: UploadFile(contentsOf: URL(fileURLWithPath: "chart.png")))
let content = [ContentBlock(text: "Numbers are in", media: [uploaded.asMediaItem()])]Scheduling and publishing
status is .draft or .scheduled, and a scheduled post needs scheduleAt. To send something out now, create it and call publish. Nothing reaches a platform without one of those two.
let post = try await client.posts.create(
CreatePostRequest(
workspaceID: workspace.id,
accounts: accounts.map(\.id),
content: .text("Ship day"),
status: .scheduled,
scheduleAt: Date().addingTimeInterval(3600)))publish returns when delivery is queued, not when the post is live. Poll client.posts.deliveries(post.id) or subscribe to webhooks for the outcome. publish(_:dryRun: true) validates the plan without sending anything, and preflight reports per-account blockers and advisory content signals.
Resources
| Namespace | Covers |
|---|---|
client.posts | List, create, update, publish, cancel, retry, preflight, deliveries, publish runs, per-post analytics, bulk actions, CSV import |
client.workspaces | Workspaces and their follower and post roll-up |
client.accounts | Connected accounts, health, validation, token refresh, history |
client.communities | The X communities an account can post into |
client.labels | Campaign labels |
client.webhooks | Outbound event subscriptions |
client.analytics | Overview, time series, top posts, posts table, labels, demographics, posting streak, on-demand collection |
client.automations | Automations, runs, stats, manual triggers |
client.media | The media library and uploads |
Pagination
Lists that paginate return a Page<T> carrying data and meta (currentPage, perPage, total, lastPage, from, to). client.posts.all(_:) walks every page as an AsyncThrowingStream:
for try await post in client.posts.all(PostListParams(status: .published)) {
print(post.id)
}Concurrency
FoPostClient is Sendable, and so is every model and request type. The whole public API is async/await, the package builds in Swift 6 language mode, and one client is safe to share across tasks and actors.
Configuration
let client = try FoPostClient(
apiKey: key,
configuration: FoPostConfiguration(timeout: 60, maxAttempts: 5))Set FOPOST_BASE_URL to point the SDK at another deployment; it defaults to https://api.fopost.com/v1.
Three attempts by default, so two retries, on 429, 5xx, and network errors. Nothing else is retried, so a 400 or a 404 comes back immediately. Backoff is exponential from 500 ms, doubling, capped at 60 seconds; a Retry-After header on a 429 wins, capped the same way. A cancelled task is never retried.
Errors
Every failure is a FoPostError, mapped from the response status:
do {
try await client.posts.publish(post.id)
} catch let error as FoPostError {
switch error {
case .paymentRequired(let details):
print(details.message, error.upgradeURL as Any)
case .rateLimited:
print("retry after \(error.retryAfter ?? 0)s")
case .validation(let details):
print(details.field("fields", as: [String].self) ?? [])
default:
print(error.localizedDescription)
}
}| Case | Status |
|---|---|
.validation | 400, 422 |
.authentication | 401 |
.paymentRequired | 402, carries upgradeURL |
.permissionDenied | 403 |
.notFound | 404 |
.conflict | 409 |
.rateLimited | 429, carries retryAfter |
.server | 5xx |
.api | anything else |
.transport | the request never reached the API |
.decoding, .encoding, .configuration | client side |
Each API case carries status, code, message, the raw body, the rate-limit budget, and field(_:as:) for anything the SDK does not model.
Escape hatch
Anything the client does not wrap yet is still reachable, with the same auth, retries, and error handling:
let payload = try await client.request(
method: "GET", path: "/some/new/endpoint", query: ["days": "30"], as: JSONValue.self)Pass any Encodable as body, and unwrapData: false when the endpoint does not use the {"data": ...} envelope.
Next
Related documentation
- SDKs & Integrations
Official FoPost clients for TypeScript, Python, PHP, Ruby, Go, Rust, Java, .NET, Swift, Kotlin, Dart, and Elixir, framework integrations from Laravel to Next.js, and tooling for the CLI, CI, Terraform, and the automation platforms.
- SDKs Overview
Every official FoPost client, what it covers, and how to pick one.
- TypeScript SDK
The official TypeScript and Node.js client for the FoPost API.
- Python SDK
The official Python client for the FoPost API.
- PHP SDK
The official PHP client for the FoPost API, with no framework and no HTTP library.
- Ruby SDK
The official Ruby client for the FoPost API, with no runtime dependencies.
- Go SDK
The official Go client for the FoPost API.
- Rust SDK
The official Rust client for the FoPost API, async and built on reqwest.