Skip to content

SMOODEV-2389: Mobile SDK parity — Swift sync publicValue, step clamp, typed errors#123

Merged
brentrager merged 2 commits into
mainfrom
SMOODEV-2389-mobile-sdk-parity
Jul 8, 2026
Merged

SMOODEV-2389: Mobile SDK parity — Swift sync publicValue, step clamp, typed errors#123
brentrager merged 2 commits into
mainfrom
SMOODEV-2389-mobile-sdk-parity

Conversation

@brentrager

@brentrager brentrager commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Problem

The Swift/Kotlin mobile SDKs (ADR-074, docs/Mobile-Runtime-Mode-Spec.md) shipped as twins, but a parity audit against the spec and the TS/Rust SDKs found four gaps:

  1. Swift publicValue(forKey:) required awaitSmooConfig was an actor, so the boot-config read was actor-isolated, violating spec row 2 (resolves synchronously, offline). The Kotlin twin was correctly synchronous.
  2. step snapping missing from limit clamping in both SDKs — a step-defined limit resolved differently on mobile than TS clampLimit / Rust clamp_limit (ADR-066).
  3. No typed evaluate errors — both SDKs collapsed failures to a bare status int, so callers couldn't branch on 404 (key not in schema) vs 400 (bad context) vs 5xx, and the key wasn't attached.
  4. Kotlin leaked its Ktor HttpClient — never closed.

Solution

  • Swift SmooConfig: actor → lock-guarded final class (the structural twin of Kotlin's @Volatile/@Synchronized). publicValue is now synchronous; async evaluate paths unchanged for callers.
  • evaluateLimit in both SDKs gains step — snaps to the nearest multiple before the [min, max] clamp, same order as the TS/Rust references.
  • Typed evaluate errors carrying the key in both SDKs: Swift enum cases (featureFlagNotFound/featureFlagContext/featureFlagEvaluation + limit*), Kotlin exception hierarchy (FeatureFlagNotFoundException et al. extending SmooConfigException).
  • Kotlin SmooConfig implements Closeable.
  • Drive-by fix: SMOODEV-2382: Mobile SDK consumption enablers (ADR-074) #120 committed 2302 files of root .build/ SPM module cache (.gitignore only covered swift/.build/). pcm files bake absolute module-cache paths, so swift test at the root manifest fatal-errors on any other machine. Untracked + ignored.

Verification

  • swift test — 9/9 green at both the root manifest (CI's entry point) and swift/
  • kotlin && ./gradlew test — 10/10 green
  • New tests: Swift compile-level sync proof (no await on publicValue), step-snaps-before-clamp (both), typed 404/400/5xx errors with key (both), Kotlin close()

Jira: SMOODEV-2389 · Pearl th-7e81f2 · Follow-up: bump the SPM revision + JitPack coordinate pins in the SMOODEV-2382-mobile-config-wiring branch (apps/mobile) once this lands.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LE7V35vzRiHyxBFE8wAxb4

brentrager and others added 2 commits July 8, 2026 16:19
…s other machines

The root SPM manifest change (SMOODEV-2382, #120) committed 2302 files of
.build/ module cache because .gitignore only covered swift/.build/. pcm
files bake absolute module-cache paths, so any other checkout fails
'swift test' at the root manifest with 'precompiled file ... was compiled
with module cache path ...' fatalError. Untrack and ignore.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE7V35vzRiHyxBFE8wAxb4
… typed errors, Closeable

The parity audit against docs/Mobile-Runtime-Mode-Spec.md and the TS/Rust
SDKs found four gaps:

- Swift SmooConfig was an actor, so publicValue(forKey:) required await —
  violating spec row 2 (boot config resolves synchronously, offline). Now a
  lock-guarded final class, the structural twin of Kotlin's
  @Volatile/@synchronized. Async evaluate paths are unchanged for callers.
- evaluateLimit in both SDKs clamped to [min,max] but skipped step snapping,
  so a step-defined limit resolved differently on mobile than TS clampLimit /
  Rust clamp_limit (ADR-066). step now snaps to the nearest multiple BEFORE
  the clamp, same order as the references.
- Evaluate failures collapsed to a bare status int. Both SDKs now throw the
  typed hierarchy (flag/limit × notFound/context/evaluation) carrying the
  key, mirroring TS FeatureFlagEvaluationError et al.
- Kotlin's Ktor HttpClient leaked; SmooConfig is now Closeable.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LE7V35vzRiHyxBFE8wAxb4
@changeset-bot

changeset-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: aefa958

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@brentrager brentrager merged commit 52b574a into main Jul 8, 2026
3 checks passed
@brentrager brentrager deleted the SMOODEV-2389-mobile-sdk-parity branch July 8, 2026 20:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant