Skip to main content

Performance

This guide covers performance optimization strategies for the Privacy Boost iOS SDK. The native binary is small enough that bundle size is rarely the bottleneck — the costs to manage are SDK init, proof generation, balance refresh frequency, and main-thread hygiene.

SDK Lifecycle

One Instance Per Process

PrivacyBoost holds a JWT, identity keys, and an internal note cache. Initialize it once and hold it in a long-lived owner — usually an @MainActor singleton or an environment object.
Re-instantiating the SDK on every screen would re-derive caches, refetch the merkle tree, and force a fresh JWT.

Defer Init Until First Use

If your app has flows that don’t touch the privacy pool (onboarding, marketing screens), defer SDK construction until the first gated route. The Rust core does some setup work on first call — keep it off the cold-start path.

Off the Main Thread

All SDK operations are async throws. Run them via Task { ... } or .task { ... } modifiers, never on the main run loop. A misplaced try await inside a synchronous function will block the actor it’s called on; let SwiftUI’s structured concurrency schedule it.
For long-running proofs in widgets or notification extensions, hop off the main actor explicitly:

Balance Refresh

Don’t Poll From Every Screen

getBalance(tokenAddress:) and getAllBalances() hit the network. Centralize refreshes through a single observable store and let screens subscribe.

Refresh on Foreground, Not on a Timer

Repeated background polling drains battery. Refresh on scenePhase == .active and after operations that change balances:

Coalesce Concurrent Refreshes

If multiple views hit refresh simultaneously, deduplicate so only one network call happens:

Network

Parallelize Independent Reads

Sequential awaits serialize unnecessarily. Use async let:

Page History — Don’t Fetch It All

Pass a small limit and use the returned cursor for pagination instead of asking for the full history at once:

Memory

Drop Heavy Caches When Backgrounded

If your app stays in the background long enough to receive a memory warning, the SDK’s internal state is the second-largest consumer after image caches. Clear non-identity state on .didReceiveMemoryWarning or scene-phase transitions.

Don’t Hold Transaction Arrays in @State

For long histories, store an IdentifiedArray keyed by txHash and flush old entries beyond the visible window. SwiftUI re-diffs @State arrays on every change.

Operation Timing

Wrap SDK calls to observe timing in development:
Hook into MetricKit (MXMetricManager) in production to surface slow operations as histograms rather than print statements.

What the SDK Already Optimizes

Things you do not need to layer on top:
  • Note cache — the SDK caches unspent notes locally and only refetches new merkle tree leaves.
  • Merkle tree pruning — proven nodes are kept; the rest are pruned.
  • JWT reuseclearSession() keeps identity keys so the next authenticate() is fast.

Best Practices Summary

  1. One SDK instance per process — hold it in a long-lived owner.
  2. Defer SDK init until the first privacy-pool screen.
  3. All operations on a Task — never on the main run loop.
  4. Refresh balances on foreground and after writes, not on a timer.
  5. Deduplicate concurrent refreshes through an actor.
  6. Parallelize independent reads with async let.
  7. Page transaction history; don’t fetch it all.
  8. Use clearSession() over logout() when re-auth is expected soon.

Next Steps