Product

What Pharen does, exactly.

Shipping an iOS app is one loop: cut a build, get it onto devices, watch what happens, fix what breaks. Most stacks split that loop across three or four vendors — the crash lives in one tool, the session that led to it in another, and the build it came from belongs to nobody. Pharen keeps the loop whole: every event shares one envelope, one consent model, and one release registry, so a crash arrives already tied to its build, its session, and its user. The five sections below are the loop, in exact behavior — and where a detail matters, the docs carry the full account.

01 · Crash reporting

Captured at crash time. Delivered next launch. Counted once.

A crashing process can’t be trusted to make a network request on its way down. Pharen writes a compact report to disk at crash time and uploads it the next time your app launches. The report file is the durable queue — deleted only when the server confirms a terminal outcome.

Every report gets a deterministic id derived from the report itself, so retries across launches re-send the same id and the server answers duplicate. A crash is counted once, no matter how many launches it takes to get through. Nothing to configure, nothing to clean up.

  • Uncaught exceptions and the fatal POSIX signals, with call stack, binary images, and the identity of the build that died — read at crash time, not upload time.
  • Handlers chain: existing handlers still run, signals re-raise, the OS crash report is still produced.
  • Grouping fingerprints the symbolicated stack and ignores volatile parts. Issues track per release — a resolved bug that reappears is flagged as a regression, not silently re-counted.
  • Late dSYMs retroactively re-group issues you already collected, with real function names.
swifthandled errors, with context
Pharen.shared?.crashes.recordError(
    "Checkout failed: payment gateway timeout",
    stack: Thread.callStackSymbols,
    severity: "error",
    breadcrumbs: ["tapped_pay", "network_slow"]
)
The delivery guarantee, plainly

Reports keep their original id and re-send until acknowledged. The server deduplicates by id. Re-running, relaunching, or retrying can never double-count a crash.

02 · Distribution

The whole release in one command.

One step archives your app, uploads the build, mints an over-the-air install link, registers the release, and uploads every dSYM the archive produced — so crashes from that build are readable from the moment it’s installed. Send the link; a tester taps it in Safari and the app installs. No App Store, no cable, no login.

  • Install links are served from a locked, CDN-fronted edge host at unguessable paths — shareable, not enumerable.
  • Builds and dSYMs upload straight to edge object storage over presigned URLs — multi-megabyte artifacts never squeeze through an app server.
  • Version, build number, and bundle id are read from the archived Info.plist, so uploaded metadata can never drift from what was built.
  • The one secret is an auth token in your CI environment. Org and app live in a committed .pharen.yml that grants nothing on its own.
  • Prefer fastlane? The plugin is MIT-licensed and published to RubyGems — one pharen_release call after build_app.

The underlying CLI is provided when we onboard your team, together with your credentials.

bashthe release lane
$ PHAREN_AUTH_TOKEN=… bash release/release-lane.sh
→ archiving (Release) · exporting .ipa
→ upload-build · registering release 1.2.0 (42)
→ upload-symbols · 3 dSYMs
itms-services://?action=download-manifest
  &url=https://dl.pharen.ai/…/manifest.plist
Environments are decided at build time
Configuration
Ingest key
Lands in
Debug
phi_test_…
development
Release
phi_live_…
production

The environment derives on the server from the key compiled into the build — a Debug build physically cannot write to production data.

03 · Sessions & analytics

One envelope. One timeline. The whole experience.

Sessions manage themselves: start at launch, end on background, resume within 30 minutes — the standard mobile rule, no code. Two calls cover your own product analytics: track() for named events, screen() for screen views.

Because every event shares one strict envelope, crashes, screens, and custom events sit in the same timeline in the event explorer, carry the same session id, roll up into session summaries, and filter the same way. A crash isn’t a separate universe from the behavior that led to it.

  • Batched and sent off the main thread; persisted to disk as queued, so a killed app delivers on next launch.
  • Every event has an id the server deduplicates on — a retry never double-counts.
  • Properties are for dimensions and metrics, not people: the SDK drops obvious identifying keys before sending, and the server enforces the rule regardless.
swifttwo calls, that's the API
Pharen.shared?.track("checkout_completed", properties: [
    "order_value": 49.99,
    "items": 3
])

Pharen.shared?.screen("Cart")
In the console

Events arrive as a live stream — type, properties, session, device context — and sessions roll up into summaries: duration and the events they contain. Segment views by release, environment, and event dimensions; acquisition-channel segmentation is on the roadmap, stated plainly as such.

05 · Identity & consent

Know your users without carrying their PII.

Every event carries a stable, pseudonymous install id — no login required. Call identify() and events also carry your customer id, connecting a person’s activity across sessions and devices.

Traits route to a restricted attribute store, separate from events. They are never inlined into event properties and never written to logs. The event stream carries only the pseudonymous id.

The full consent architecture →
swiftidentity, separated from events
Pharen.shared?.identify("cus_482", traits: ["plan": "pro"])

// events carry:      cus_482  (pseudonymous id)
// restricted store:  plan=pro (never on the stream,
//                    never in logs)

Want to see it against your app?

We’ll set up keys, your first release, and your console. hello@pharen.ai

Request early access