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.

Building for Android? The Android SDK is in private beta.

1. Add the SDK

SDK license. Before obtaining or using the SDK, read its proprietary license (Schedule A, Part C) and the LICENSE.md and LEGAL-TERMS.txt supplied with your selected release. For organizational use, an authorized representative should agree through onboarding or arrange written acceptance with hello@pharen.ai. Automated package resolution is not a record of that agreement.

PharenSDK is distributed with Swift Package Manager (Apple system frameworks — zero third-party dependencies). In Xcode, File → Add Package Dependencies…, or add it to your Package.swift:

swiftPackage.swift
dependencies: [
    .package(url: "https://github.com/pharen-ai/pharen-sdk-swift.git", from: "2.0.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

bash
pharen setup ios

No 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 uses a registered test environment, often test; existing projects may use development. Release uses live. The command prints the Integration status URL for the environment your Debug build uses; keep that scope for the agent connection below.

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.

textConfig/Pharen.xcconfig (generated)
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-KEY

Your 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:

xmlInfo.plist
<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:

swiftMyApp.swift
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 PharenConsentGrantedAtBuild 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:

bash
pharen verify --wait

verify checks the things that otherwise fail in silence — a missing or malformed key, a key whose environment 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 live to watch a different environment, or --timeout 60s to wait less.

6. Connect your agent

With events arriving, the agent you work with can ask about them. Mint a read-only key for the environment you are watching and connect any MCP client — the same registry the console reads, so the two never disagree. Use the app id and environment in the Integration status URL printed by pharen verify --wait. This example uses test; substitute your actual environment if it differs:

bash
pharen query-keys create --environment test --app your-app
# then point your MCP client at https://mcp.pharen.ai/mcp with the key

Keep the returned key private. Connect your MCP client with that key, then ask it to call pharen_list_metrics and pharen_apps. For the first local run, ask for the ungrouped events.count total in the same environment. It includes local Debug runs without extra grants. Use Integration status or the CLI receipt to verify the specific build. Customer metrics default to released builds, so they may be empty while a Debug build is sending. No additional read grants are needed.

Client-specific connection commands and the data your agent can read: Connect your agent.

Optional: test crash reporting

The SDK ships a deliberate crash for exactly this moment. Wire it to a button in a Debug build:

swift
#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.

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