open-feature / open-feature/swift-sdk
[Multi-provider] Gaps identified relative to js-sdk reference implementation
- Dominant language
- Swift
- Stars
- 31
- Forks
- 14
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 2
Description
## Context
We conducted a cross-SDK comparison of all MultiProvider implementations using the js-sdk as the reference. The Swift MultiProvider is functional for basic use cases, but we identified several gaps relative to the reference implementation.
Note: Some gaps (like shutdown propagation) are blocked by SDK-level prerequisites that are tracked separately (#97).
## Gaps
### 1. Aggregate event/status tracking (High)
The MultiProvider merges child provider event streams via `Publishers.MergeMany`, which forwards raw events directly to subscribers with no aggregation. If one child provider emits `.error` while another is `.ready`, both events pass through independently and there is no composite status. Consumers have no way to know the overall health of the MultiProvider.
**Expected behavior:**
- Maintain a per-provider status map
- Compute an aggregate status using "worst-wins" precedence: `FATAL > NOT_READY > ERROR > STALE > RECONCILING > READY`
- Only emit an event when the aggregate status actually changes (deduplication)
- Always forward `.configurationChanged` events as a pass-through regardless of status changes
**Reference:** js-sdk `status-tracker.ts`, dotnet-sdk `HandleProviderEventAsync` / `DetermineAggregateStatus`, kotlin-sdk `handleProviderEvent` / `calculateAggregateStatus`
### 2. Per-provider hook execution during evaluation (High)
The `hooks` property returns an empty array. The strategy calls provider evaluation methods directly, bypassing any hooks the child providers may define. If a child provider returns hooks via its `hooks` property, those hooks are not executed.
**Expected behavior:**
- Before evaluating a child provider, run its `before` hooks with an isolated copy of the hook context
- On success: run `after` hooks
- On error: run `error` hooks
- Always: run `finally` hooks
- Hook context must be isolated per-provider to prevent cross-provider mutation
**Reference:** js-sdk `hook-executor.ts`, go-sdk `isolation.go`, dotnet-sdk `ProviderExtensions.EvaluateAsync`
### 3. Tracking event forwarding (High)
The MultiProvider relies on the default no-op `track()` extension on `FeatureProvider`. Tracking events are not forwarded to child providers.
**Expected behavior:**
- Iterate over child providers and forward `track()` calls
- Skip providers that are not in a ready/active state
- Errors from individual `track()` calls should be caught and logged, not propagated
**Reference:** js-sdk `multi-provider.ts` `track()`, dotnet-sdk `MultiProvider.cs` `Track()`
### 4. ComparisonStrategy (Medium)
Only `FirstMatchStrategy` and `FirstSuccessfulStrategy` exist. There is no `ComparisonStrategy` for evaluating all providers and comparing results (useful for migration validation and consistency checks).
**Expected behavior:**
- Evaluate all providers (ideally in parallel)
- If all providers agree on the value, return it
- If providers disagree, call an optional `onMismatch` callback and return the designated fallback provider's result
- If any provider errors, collect and report all errors
- Constructor accepts a `fallbackProvider` and optional `onMismatch` callback
**Reference:** js-sdk `comparison-strategy.ts`, go-sdk `comparison_strategy.go`, dotnet-sdk `ComparisonStrategy.cs`
### 5. Unique provider naming with deduplication (Low)
There is no unique naming or deduplication for child providers. If two providers share the same metadata name, there is no disambiguation. This affects event attribution and debugging.
**Expected behavior:**
- If multiple providers share the same metadata-derived name, auto-deduplicate with a numeric suffix (`name-1`, `name-2`, etc.)
- Expose child provider metadata keyed by unique name
**Reference:** js-sdk `registerProviders()`, dotnet-sdk `RegisterProviders()`, kotlin-sdk `toChildFeatureProviders()`
## Blocked / Deferred
- **Shutdown propagation**: Requires SDK-level shutdown support first (#97)
## Spec Reference
https://openfeature.dev/specification/appendix-a/#multi-provider
Contributor guide
Research direction
Start with the Swift MultiProvider implementation, its Publishers.MergeMany event handling, hooks property, track() forwarding, and existing FirstMatchStrategy and FirstSuccessfulStrategy entry points. Compare each behavior with the cited reference implementations, then define completion as covering the four high/medium gaps and unique provider naming while leaving shutdown propagation blocked by #97.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- api
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 32/100