iOS quickstart
Your first crash report in about five minutes
Pharen is crash reporting, build distribution, and product analytics for iOS, behind one small SDK with no third-party dependencies. This page takes you from an empty project to a real crash report you can open. Everything here runs against the shipping SDK — the code compiles as written.
1. Add the SDK
PharenSDK is distributed with Swift Package Manager (Foundation only — zero third-party dependencies). In Xcode, File → Add Package Dependencies…, or add it to your Package.swift:
dependencies: [
.package(url: "https://github.com/pharen-ai/sdk-ios.git", from: "0.1.0")
]Then add "PharenSDK" to your app target's dependencies. Pin a SemVer major range. (On an XcodeGen project you can skip this — setup in the next step offers to add the package and link it for you.)
2. Get your keys — one command, no copy-paste
pharen setup iosNo arguments. That signs you in (approve in any browser), issues one ingest key per environment, and writes them into a generated Config/Pharen.xcconfig, keyed by build configuration — Debug reports to development, Staging to staging, Release to production. You never see a key, retype one, or decide which goes where.
There is nothing to look up first. Your org comes from your credential, and your app id is proposed from your project's own bundle identifier — the whole identifier, lowercased, so com.Acme.MyApp becomes com.acme.myapp and two apps can never collide. It shows you the id and asks before using it. Pass --app to choose your own, which always wins.
If your project is built from a project.yml (XcodeGen), it then offers to wire everything up for you — pointing the project at the config, adding the SDK package, and declaring the Info.plist key. It prints every edit and asks first, so you can decide whether your tree is clean enough for it. Say no and nothing is written; your keys are minted either way.
Re-running pharen setup ios at any point is safe — it skips whatever is already done and picks up from there.
PHAREN_INGEST_KEY = YOUR-DEVELOPMENT-KEY
PHAREN_INGEST_KEY[config=Debug] = YOUR-DEVELOPMENT-KEY
PHAREN_INGEST_KEY[config=Staging] = YOUR-STAGING-KEY
PHAREN_INGEST_KEY[config=Release] = YOUR-PRODUCTION-KEYYour project has to read that file. On an XcodeGen project setup offers to do it; otherwise set it in Xcode under Project → Info → Configurations, for every configuration. pharen verify tells you if it isn't wired.
3. The Info.plist key
Pharen configures itself from your app's Info.plist — no config code. The key points at the build setting rather than holding a value, which is what gives each build configuration its own environment:
<key>PharenIngestKey</key>
<string>$(PHAREN_INGEST_KEY)</string>setup writes this for you on an XcodeGen project. Add it by hand otherwise — and note the indirection is required, not stylistic: Xcode's INFOPLIST_KEY_* build settings only carry Apple's own keys, so a custom one cannot come from the build configuration directly.
Consent is yours to grant, and setup deliberately won't do it for you — see step 4.
4. Start Pharen
One line, as early as possible in your app's lifecycle:
import PharenSDK
import SwiftUI
@main
struct MyApp: App {
init() {
// Reads PharenIngestKey from Info.plist; you grant what you collect.
Pharen.start(granting: [.telemetry, .behavioral])
}
var body: some Scene {
WindowGroup { ContentView() }
}
}This is the one edit that stays yours. Granting consent purposes is a policy decision, so it belongs in a line a reviewer will see — not in a file an installer wrote. telemetry covers crashes; behavioral covers sessions and track(). You can also declare them in PharenConsentGranted in the plist if you prefer.
That's the whole install. Pharen.start() installs the crash handlers, starts a session, and begins delivering events in the background — off the main thread, in batches. If the app is unconfigured it returns nil and installs nothing.
5. Check it — and see your first event
Build and run the app once, then ask Pharen whether any of this actually worked:
pharen verify --waitverify checks the things that otherwise fail in silence — a missing or malformed key, a key whose ring doesn't match its build configuration, a value the // rule truncated, a build configuration file nothing reads, a missing Info.plist entry, and a key that has since been revoked. Every check reports pass, fail, warn, or skipped, so a check that couldn't run says so instead of quietly counting as a pass. It exits non-zero on any failure, so it works as a CI gate too.
--wait then watches for your first event and prints it — including the version and build it came from, so you know it was this build talking. That is the confirmation that the integration is live. Add --environment production to watch a different ring, or --timeout 60 to wait less.
6. Trigger a test crash
The SDK ships a deliberate crash for exactly this moment. Wire it to a button in a Debug build:
#if DEBUG
Button("Force a test crash") {
Pharen.crashTest() // raises an uncaught exception on purpose
}
#endif
Run the app on a device (not the debugger — an attached debugger catches the signal first), tap the button, and let it crash.
7. Reopen the app — and look
Fatal crashes are written to disk at crash time and uploaded on the next launch (a crashing process can't reliably send anything itself). So relaunch the app once. Within moments the crash appears in your Pharen console, grouped into an issue.
Where to next
Crash reporting
What's captured, symbolication, next-launch delivery, and grouping into issues.
Distribution
Ship a build with an OTA install link in one command; TestFlight and rings.
Sessions & analytics
Automatic sessions plus
track()andscreen()events.Privacy & consent
Consent on every event, server-side identity, and no PII on the stream.