Skip to main content

Swift Performance

Finding

The optimized Swift facade is neutral or faster for the shared crypto workloads. Identity import is 7.38% faster after removing the generic combined record path; the cached public-key getter is 91.90% faster. Parse-and-verify is 6.96% slower at the median and 12.49% slower at p95, within the descriptive 10%/15% latency budgets and not reproduced as a native Rust regression.

The audit used three paired optimized processes on a GitHub-hosted arm64 macos-15 runner with Swift 6.1.2. Values are nanoseconds per authored SoftchatKit call. Negative deltas mean the current SDK is faster.

OperationBytesOld median nsCurrent median nsDeltaCurrent p95 nsp95 delta
Import identity3217,87116,552-7.38%17,979-1.18%
Read cached public key024720-91.90%20-92.22%
NIP-44 encrypt3245,97944,054-4.19%44,490-5.50%
NIP-44 decrypt3243,58743,153-1.00%43,286-6.57%
NIP-44 encrypt1,02453,39948,900-8.43%49,421-13.90%
NIP-44 decrypt1,02452,60248,059-8.64%48,727-13.77%
NIP-44 encrypt64,000376,964366,781-2.70%379,782-8.74%
NIP-44 decrypt64,000386,037366,391-5.09%406,401+4.27%
Sign NIP-01 event3295,54992,454-3.24%97,601+0.01%
Parse and verify event37439,69642,460+6.96%45,655+12.49%

The 20-ns getter is an API-shape check, not a useful absolute latency claim.

Current-only baseline

OperationBytesMedian nsp95 ns
Create NIP-59 rumor154,8974,947
Create NIP-17 text message1515,48515,689
Create durable gift wrap15292,357295,934
Authenticate and unwrap15173,322173,632
Stream-encrypt attachment64,000681,080687,838

Optimizations retained

  • SoftchatIdentity imports through the lightweight native constructor, caches its validated public key, and avoids the Android-only registry state.
  • Encrypt and decrypt return or accept direct payload fields instead of round-tripping an aggregate generated record.
  • Immutable Swift values own received events, messages, and metadata; native records do not leak into application layers.
  • The identity lock protects handle lifetime and early erasure without making every public getter an FFI call.
  • Streaming media uses bounded chunks and delays exposing decrypted output until authentication succeeds.

Footprint

The performance harness links three unstripped dynamic libraries:

Harness library setOld bytesCurrent bytesDelta
Rust + generated FFI + SoftchatKit2,425,3364,895,304+101.84%

This is a link-input measurement, not a shipped iOS application size. The Swift package carries a static XCFramework so the final application linker can dead-strip unused code. A release archive and dSYM measurement on physical iOS is still required before setting an Apple footprint gate.

Developer guidance

  • Create one SoftchatIdentity for the authenticated session and retain its cached publicKey; do not recreate it per message.
  • Call synchronous crypto and parsing from a bounded task or executor rather than the main actor. Do not add an async wrapper that hides unbounded work.
  • Keep the identity as an ordinary ARC-owned reference. Call erase() only when authority must end early.
  • Pass complete events, envelopes, relay frames, and attachment chunks through SoftchatKit. The generated FFI module is internal.
  • Keep Keychain/Secure Enclave policy, URLSession, GRDB, background transfer, and application lifecycle in Swift.
  • For extensions, create an independent session owner and close it explicitly; do not share callbacks or secret-derived state across process boundaries.

Run the shared and current-only audit with scripts/run-swift-performance.sh, or dispatch the manual performance workflow. Any facade, FFI, package, or native-profile change requires the macOS package consumer and affected performance audit.

Open work

The macOS source, DocC, XCFramework/SwiftPM build, and package consumer are green. Remaining Apple evidence is an external application/archive/extension integration, physical-iOS latency, startup and memory, and final archive/dSYM footprint.