Skip to content

iOS Usage

kirich1409 edited this page May 31, 2026 · 2 revisions

iOS Usage

Preview status. The iOS surface (SKIE bridge, DCE xcconfig) is Preview in v1.0.0. The public API may change in minor releases without a major version bump.

SPM consumption caveat (v1.0.0)

The Package.swift manifest is updated by CI in a commit after the release tag, so resolving the Swift package by the 1.0.0 tag (.package(url: ..., from: "1.0.0")) currently fetches the previous XCFramework. For v1.0.0, consume the iOS XCFramework via the GitHub Release asset directly (download FeaturedCore.xcframework.zip from the v1.0.0 release) or pin the Swift package to the specific commit that carries the updated Package.swift, rather than the tag. This will be addressed in a future iOS-stable release.

Featured exposes its Kotlin API to Swift via SKIE. No Kotlin toolchain is needed in Xcode — the framework is built by the KMP Gradle build and consumed as a prebuilt XCFramework or Swift Package.

Import the module:

import FeaturedCore

FeaturedCore is the Swift product name. CoreConfigParam is the Kotlin ConfigParam<T> type as seen from Swift.

Initializing ConfigValues

Call initialize() before serving any screen, then trigger a background fetch:

@MainActor
class AppState: ObservableObject {
    let configValues: ConfigValues

    init() {
        configValues = ConfigValues(
            localProvider: nil,
            remoteProvider: nil,
            onProviderError: { error in print("Featured error: \(error)") }
        )
    }

    func setup() async {
        do {
            try await configValues.initialize()
            try await configValues.fetch()
        } catch {
            print("Featured setup error: \(error)")
        }
    }
}

Pass provider instances to localProvider and remoteProvider as needed. At least one must be non-nil.

One-shot async read

let configValue = try await configValues.getValue(param: FeatureFlags.shared.newCheckout)
let isEnabled: Bool = configValue.value

FeatureFlags.shared.newCheckout is a CoreConfigParam<KotlinBoolean> — the Kotlin-generated ConfigParam accessed through the Swift wrapper your team writes (see Declaring Flags).

AsyncStream (reactive)

SKIE bridges the Kotlin Flow as an AsyncStream:

for await configValue in configValues.observe(param: FeatureFlags.shared.newCheckout) {
    updateUI(configValue.value)
}

Use this inside a Task tied to your view's lifetime:

.task {
    for await configValue in configValues.observe(param: FeatureFlags.shared.newCheckout) {
        isNewCheckoutEnabled = configValue.value
    }
}

Combine publisher

Build a Combine publisher on top of AsyncStream:

import Combine

class CheckoutViewModel: ObservableObject {
    @Published var isNewCheckoutEnabled: Bool = false

    private let configValues: ConfigValues

    init(configValues: ConfigValues) {
        self.configValues = configValues
    }

    func startObserving() {
        Task {
            for await configValue in configValues.observe(param: FeatureFlags.shared.newCheckout) {
                await MainActor.run {
                    self.isNewCheckoutEnabled = configValue.value
                }
            }
        }
    }
}

If your Swift wrapper exposes a publisher(for:) method:

featureFlags.publisher(for: newCheckoutFlag)
    .receive(on: DispatchQueue.main)
    .sink { isEnabled in updateUI(isEnabled) }
    .store(in: &cancellables)

SwiftUI integration

struct CheckoutView: View {
    @StateObject private var viewModel: CheckoutViewModel

    var body: some View {
        Group {
            if viewModel.isNewCheckoutEnabled {
                NewCheckoutFlow()
            } else {
                LegacyCheckoutFlow()
            }
        }
        .onAppear { viewModel.startObserving() }
    }
}

Dead-code elimination

For release builds, Featured can physically strip code behind disabled flags at compile time. A flag declared with default = false in the KMP module generates an xcconfig condition DISABLE_<FLAG_KEY> that feeds into Xcode as a Swift compilation condition. See iOS — xcconfig DCE for the setup procedure.

Clone this wiki locally