Before you start
Create the project with the iOS integration type.
| What you need | Why it matters |
|---|---|
| iOS project in Logister | Locks the project type so collected iOS data is interpreted as mobile telemetry. |
| Backend token issuer | Keeps the server project API token out of the IPA and returns short-lived mobile ingest tokens to the app. |
| Logister instance base URL | Builds the /api/v1/ingest_events endpoint used by the SDK. |
| Xcode or Swift Package Manager | Resolves the public Git package and the Logister library product. |
| Optional GitHub App installation | Lets Logister use mobile source context and CI deployment records for source excerpts and deploy links. |
What is supported
Use explicit reports for caught errors and MetricKit for OS diagnostics.
| Evidence | Swift API | Logister behavior |
|---|---|---|
| Handled report | captureException | Appears as Reported error with the reporting thread. It is never labelled fatal. |
| Apple diagnostic | LogisterMetricKitCollector | Uploads delayed crash, hang, CPU-exception, and disk-write diagnostics with triggered threads, binary UUIDs, raw addresses, and replay-safe event IDs. |
| Log | captureMessage | Activity feed and event detail. |
| Metric | captureMetric | Insights, activity, and custom metric series. |
| Transaction | captureTransaction | Performance and Insights for screen loads, sync work, and app jobs. |
| Span | captureSpan with LogisterSpan | Request load waterfalls and detailed timing. |
| Check-in | checkIn | Monitor status for recurring mobile work. |
| Source context | repository, commitSHA, branch on LogisterClient | Source lookup and deployment context when a GitHub repository is connected to the project. |
Install
Add the package from GitHub with Swift Package Manager.
Privacy-safe error policies and minimized Apple context are available in package version 0.3.0. In Xcode, add the package URL https://github.com/taimoorq/logister-ios.git, choose the version rule your app prefers, and add the Logister product to your app target.
dependencies: [
.package(url: "https://github.com/taimoorq/logister-ios.git", from: "0.3.0")
].product(name: "Logister", package: "logister-ios")- Use the Swift Package repository for source, Package.swift, tests, and release metadata.
- Open the v0.3.0 GitHub Release for the current tagged package release.
- Swift Package Manager resolves versions from Git tags, so there is no separate iOS package registry account or package page.
Configure
Create one client with app and release defaults.
Use an async LogisterTokenProvider, the app bundle ID as service, and the app version/build in release. Do not compile a long-lived Logister project API key into the app; your backend should mint short-lived mobile ingest tokens with POST /api/v1/mobile_ingest_tokens.
import Foundation
import Logister
struct AppBackendTokenProvider: LogisterTokenProvider {
func fetchToken() async throws -> LogisterToken {
LogisterToken(
token: "short-lived-mobile-token",
expiresAt: Date().addingTimeInterval(900)
)
}
}
let client = LogisterClient(
baseURL: URL(string: "https://your-logister-host.example")!,
tokenProvider: AppBackendTokenProvider(),
environment: "production",
release: "1.4.0+42",
repository: "acme/ios-app",
commitSHA: "4f8c2d1",
branch: "main",
service: Bundle.main.bundleIdentifier,
exceptionDataPolicy: .typeAndStacktrace,
platformContextPolicy: .minimized
)Keep real project API tokens on your backend only. Use a build setting or generated file for commitSHA and branch so shipped events point at the source that produced the app build.
Source and deployments
Attach source context in the app, and record deployments from CI/CD.
The Swift client adds repository, commit_sha, and branch to event context when you set repository, commitSHA, and branch on LogisterClient. If the project is connected to the GitHub App, Logister can use those values to resolve stack frames and link to source.
Deployment records should be sent from CI/CD, not from the mobile app. After an App Store, TestFlight, Firebase App Distribution, internal, or enterprise deploy succeeds, POST a deployment envelope to /api/v1/deployments with the release, environment, repository, commit SHA, branch, and workflow URL.
{
"deployment": {
"release": "1.4.0+42",
"environment": "production",
"repository": "acme/ios-app",
"commit_sha": "4f8c2d1a9b7e6c5d4a3b2c1d0e9f8a7b6c5d4e3f",
"branch": "main",
"release_tag": "ios-v1.4.0",
"workflow_run_url": "https://github.com/acme/ios-app/actions/runs/123"
}
}Verify it
Send one test exception from a build using the same release. Open the event detail and check for source lookup status, deployment context, and GitHub links. If source is missing, confirm the GitHub App is installed, linked to the project, and the repository is connected; then check that CI sent a deployment record or the app event has repository and commit_sha.
Events
Send handled reports, logs, metrics, and transactions with async Swift.
try await client.captureMessage(
"Checkout opened",
options: LogisterEventOptions(
sessionID: "session-123",
context: ["screen_name": .string("Checkout")]
)
)
try await client.captureMetric("cart.item_count", value: 3, unit: "count")
try await client.captureTransaction(
"screen.load",
durationMs: 142.7,
options: LogisterEventOptions(context: ["screen_name": .string("Checkout")])
)
do {
try await runCheckout()
} catch {
try await client.captureException(
error,
options: LogisterEventOptions(
sessionID: "session-123",
installationIDHash: "rotating-random-pseudonym",
distributionChannel: "testflight",
inForeground: true,
breadcrumbs: [
LogisterBreadcrumb(category: "navigation", message: "Opened checkout")
]
)
)
}captureException records a caught, handled error. The SDK structures the reporting-thread frames and marks the event as nonfatal. Metric values should use stable names and units, and transactions should describe app work such as screen loads, local processing, sync jobs, or API calls.
MetricKit diagnostics
Keep one opt-in collector alive for the app lifetime.
MetricKit is the supported automatic source for Apple crash and hang evidence on the package's current deployment targets. Delivery is delayed and controlled by the operating system; it is not a real-time fatal callback.
@available(iOS 15.0, *)
final class AppDiagnostics {
let collector: LogisterMetricKitCollector
init(client: LogisterClient) {
collector = LogisterMetricKitCollector(client: client, dataPolicy: .typeAndStacktrace) { message in
print("MetricKit upload failed: \(message)")
}
collector.start()
}
deinit { collector.stop() }
}The collector uploads crash, hang, CPU-exception, and disk-write diagnostics. Safe mode omits the raw diagnostic payload and termination reason and bounds normalized threads and frames. Every payload gets a deterministic event UUID. If Apple redelivers it or a transient retry reaches Rails twice, Logister returns the original accepted event without increasing occurrence, installation, or session impact.
Delivery behavior
The SDK retries network errors, HTTP 408/425/429, and 5xx responses up to three attempts by default and honors capped Retry-After. A persistent offline queue is not included, and the SDK never reports queued work as server acceptance.
Production evidence
Manage symbols and Apple reports as separate sources.
| Settings workflow | Purpose | Healthy state |
|---|---|---|
| Project Settings → Integrations → dSYM coverage | Uploads a zipped dSYM to private archive storage and verifies its binary UUID and architecture in a background worker. | Ready. Awaiting tooling means this worker cannot run Apple dwarfdump; the inbox continues showing raw addresses. |
| Project Settings → Integrations → App Store Connect | Resolves the configured bundle ID and imports Apple's iOS power/performance report with a short-lived ES256 API token. | A selected app, last-success timestamp, report state, freshness note, and no current error. |
Store the App Store private key in the host secret store and enter only its environment-variable name in Logister. Store aggregates are not SDK occurrences and are never silently added to MetricKit or crash-free counts.
Spans and check-ins
Use spans for waterfalls and check-ins for recurring iOS work.
try await client.captureSpan(
LogisterSpan(
traceID: "trace-123",
spanID: "span-456",
parentSpanID: "span-root",
name: "GET /checkout",
kind: "http",
status: "ok",
durationMs: 42.5,
context: ["screen_name": .string("Checkout")]
)
)
try await client.checkIn(
"daily-sync",
status: "ok",
options: LogisterEventOptions(
durationMs: 812.4,
context: ["expected_interval_seconds": .number(86_400)]
)
)Use shared trace IDs when a screen action triggers multiple spans. Check-ins are useful for recurring sync, cleanup, cache refresh, background fetch, or other scheduled app work where a missed run should be visible.
Mobile context
Start with the automatic Apple context, then add only deliberate correlation.
| Context | Collection | Why it helps |
|---|---|---|
| Bundle, version/build, process, and inferred release | Automatic from Bundle.main | Release and build filtering without duplicated app configuration. |
| Apple platform, OS version/build, device family/model, architecture, locale | Automatic from platform APIs | Finds OS-, hardware-, and architecture-specific failures. |
| Session ID | Optional app-provided value | Enables affected-session impact when your privacy model permits it. |
| Installation hash | Optional rotating random pseudonym | Enables affected-installation impact without a hardware or advertising identifier. |
| Distribution, foreground, screen, breadcrumbs | Optional app-provided values | Explains the app state and bounded trail before a report. |
| Repository, commit SHA, branch | Build-time values | Connects frames and CI deployment records to exact source. |
Privacy defaults
Do not turn device identity or raw errors into monitoring context.
The SDK recursively removes common IDFA, IDFV, serial, and hardware-identifier aliases before encoding an event. Rails repeats the same normalization at ingestion. This is a guardrail, not a substitute for reviewing custom context.
- Use
typeAndStacktracefor handled errors unless raw error text has completed a privacy review. - Use
platformContextPolicy: .minimizedwhen exact model, locale, architecture, and OS build are not needed. - MetricKit safe mode omits raw payloads and termination reasons while retaining bounded frames and stable codes.
- Do not send API tokens, authorization headers, cookies, payment data, medical data, raw request/response bodies, IDFA, or raw IDFV.
- Use an app-generated rotating pseudonym only when affected-installation impact is necessary, and hash it before capture.
- Keep breadcrumbs bounded and free of typed input, raw URLs, query values, and user content.
Verification
Confirm one event from the app before widening instrumentation.
Swift Package Manager resolves the Logister product
client can send one captureMessage call
Activity page shows the iOS log event
Inbox shows a test exception when captureException is called
Insights can filter by environment, release, service, or screen_name
Performance shows transactions or spans after timing events arrive
Event detail shows source context when repository and commitSHA are configured
Event detail shows deployment context after CI posts /api/v1/deployments
Manual capture is labelled Reported error, not Fatal
MetricKit test evidence shows its source and does not duplicate on redelivery
dSYM status shows Ready or an explicit worker-tooling action
App Store settings show provenance, last success, freshness, and errorsPackage releases
iOS SDK releases are versioned separately from the Rails app.
Swift Package Manager distribution resolves from the public Git repository and semantic version tags. The iOS release workflow verifies the package and creates the matching GitHub Release from the checked-in changelog, so app teams can update through Xcode or SwiftPM when a new tag is available.
- SDK source and SwiftPM URL: github.com/taimoorq/logister-ios
- Current release: v0.3.0
- Package product:
Logister