iOS
Gatepost for iOS is a Swift framework with a small Objective-C runtime layer. It supports iOS and iPadOS 14 and later, SwiftUI and UIKit, and adds about 380 KB to an arm64 build.
Requirements
- iOS or iPadOS 14.0 or later on the device.
- Xcode 15 or later, Swift 5.9 or later. Objective-C projects are supported through the generated header.
- Current SDK version: 2.9.1. See the changelog.
Installation
The SDK is distributed from an authenticated package repository. The repository URL and the credentials for your organisation are shown in the console under Project settings, SDKs. Both installation methods below use them.
Swift Package Manager
In Xcode choose File, Add Package Dependencies, paste the package URL from the console and select the Gatepost product. In Package.swift:
dependencies: [
.package(url: GATEPOST_PACKAGE_URL, from: "2.9.0")
],
targets: [
.target(name: "ShopApp", dependencies: [
.product(name: "Gatepost", package: "gatepost-ios")
])
]
XCFramework
Download Gatepost.xcframework.zip from the same page, unzip it and drag the framework into Frameworks, Libraries and Embedded Content with Embed and Sign. The archive is signed; Xcode verifies the signature when it is added.
Starting the SDK
Start Gatepost before any other framework that creates URLSessions, so the first requests are instrumented.
import Gatepost
@main
struct ShopApp: App {
init() {
Gatepost.start(projectKey: "4f9c2e1b8a7d63e0") { config in
config.environment = .production
config.sampleRate = 1.0
}
}
var body: some Scene {
WindowGroup { RootView() }
}
}
import Gatepost
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
Gatepost.start(projectKey: "4f9c2e1b8a7d63e0")
return true
}
Configuration
All options have defaults that suit a production app. Anything marked remote can also be changed from the console and takes effect on the next app launch without a new build; the remote value wins over the value in code.
| Option | Default | Description |
|---|---|---|
sampleRate | 1.0 | Fraction of sessions recorded, 0 to 1. Decided once per session. Remote. |
environment | .production | .production, .staging or .development. Development sessions are kept for 24 hours and never count towards the plan. |
trackScreens | true | Automatic screen detection for UIViewController and SwiftUI navigation. |
trackNetwork | true | Instrument URLSession. Request and response bodies are never read. |
trackCrashes | true | Install Mach exception and signal handlers. Disable if another crash reporter is installed. |
trackFrames | true | Slow and frozen frame detection through CADisplayLink. |
ignoredHosts | [] | Hosts whose requests are not recorded, for example an analytics vendor or a certificate pinning probe. Remote. |
uploadInterval | 30 | Seconds between uploads while the app is in the foreground. Minimum 10. Remote. |
maxBatchBytes | 262144 | Upper bound of one compressed upload. Larger batches are split. |
release | bundle version | Override the release identifier, for example to append a git SHA. |
user | nil | An opaque identifier for your user, hashed on device before upload. Optional. |
Screens
With trackScreens enabled, every UIViewController that appears becomes a screen named after its class, and SwiftUI navigation destinations are named after their view type. To give a screen a stable readable name, or to mark a screen that is not a view controller, call screen yourself:
Gatepost.screen("Checkout")
// SwiftUI
CheckoutView()
.gatepostScreen("Checkout")
Rendering metrics for a screen are measured from the moment it is named until the first frame after its content is laid out. If a screen loads its content asynchronously, mark the moment the content is visible so the metric reflects what the user saw:
Gatepost.screenReady("Checkout")
Network requests
URLSession requests are recorded automatically through a task metrics hook, not by intercepting traffic. For each request Gatepost stores the method, host, route template, status code, DNS, connect and TLS handshake times, time to first byte, total duration and the size of the request and response bodies. Bodies, headers and query strings are never read.
Route templates are derived by replacing numeric and UUID path segments with placeholders, so /orders/8123/items becomes /orders/{id}/items. To provide your own template, set it on the request:
var request = URLRequest(url: url)
request.gatepostRoute = "/orders/{id}/items"
Third-party HTTP clients built on URLSession are covered. Clients that use their own sockets can report requests manually through Gatepost.network(...).
Custom traces
let trace = Gatepost.trace("checkout.submit")
trace.set("items", cart.count)
trace.set("payment", method.rawValue)
let validation = trace.child("validate")
try validate(cart)
validation.end()
try await api.submit(cart)
trace.end() // or trace.end(error: error)
Traces are limited to 64 attributes and 256 child spans; anything beyond that is dropped and counted in the SDK diagnostics. Trace names should be stable identifiers, not sentences: checkout.submit, not Submitting the checkout for user 42.
Crashes
Gatepost installs Mach exception and signal handlers at start and writes a minimal crash record to disk synchronously, in an async-signal-safe way. The record is uploaded together with the last 30 seconds of the session on the next launch. Crashes are grouped by the top in-app frame after symbolication.
If you run another crash reporter, set trackCrashes = false. Two sets of handlers in one process leads to one of them losing reports.
Uploading dSYM files
Symbolication needs the dSYM for each build. Add a run script phase after Embed Frameworks, or run the command from CI after archiving:
gatepost-cli upload-dsym \
--project-key 4f9c2e1b8a7d63e0 \
--path "${DWARF_DSYM_FOLDER_PATH}"
The CLI is available from the SDKs page in the console. If Bitcode recompilation or App Store Connect produces new dSYMs, download them from App Store Connect and upload them the same way.
Privacy manifest
The SDK ships with a PrivacyInfo.xcprivacy declaring the data it collects and the required reason APIs it uses. Xcode merges it into the app's privacy report automatically.
| Entry | Value |
|---|---|
| Collected data types | Crash data, Performance data. Not linked to identity, not used for tracking. |
| Tracking | None. The SDK never reads the advertising identifier. |
| Required reason APIs | User defaults (CA92.1), file timestamp (C617.1), system boot time (35F9.1) for monotonic timing. |
See data and privacy for the full list of fields the SDK sends.
Troubleshooting
- No sessions in the console. Enable debug logging with
config.logLevel = .debugand look for the upload response.401means a wrong key; a timeout means the collector host is blocked on that network. Open/diagnosticson the collector from the device to confirm. - App start looks too long. Cold start is measured from process creation, so time spent in
dyldand static initialisers counts. Compare the phase breakdown in the console rather than the total. - Crashes are not symbolicated. Check that the dSYM UUID matches the build; the console shows missing UUIDs under the crash group.