Cookbook

Supporting iOS 15 and 16

Undra's Swift output is iOS 17 by default, because a generated store is @Observable. Lower the deployment target and the same store becomes an ObservableObject: the core, the wire and the commands do not change, only how SwiftUI is told to redraw.

Turn it on#

undra.toml
[ios]
deployment_target = "15.0"   # 15.0 or later; below 17.0 the stores are ObservableObjects

Run undra bindgen. A new app starts there with undra init todo --ios-deployment-target 15.0. [bindings] swift_observation = "observable-object" (or --swift-observation) picks the shape on any floor; "observation" with a target below 17.0 is an error that names both settings. Below iOS 16 the wire Duration is the runtime's UndraDuration (exact nanoseconds), because Swift.Duration is iOS 16.

Your views#

iOS 17+ (observation)iOS 15 and 16 (observable-object)
owning view@State private var todos = try! Todos()@StateObject private var todos = try! Todos()
child viewlet todos: Todos@ObservedObject var todos: Todos
environment.environment(todos).environmentObject(todos)
outside SwiftUIwithObservationTrackingtodos.$visible.sink { … }
connection statecore.connection.statecore.connectionObject with @ObservedObject

Reading a signal is still a plain property read and a command is still a method call. The $property publishers are a gain for UIKit and Combine code on every floor.

What degrades#

Raising the floor later#

Set deployment_target = "17.0", run undra bindgen and make the mechanical edits in the table. No core change, no wire change. The repository's examples/ios15-sample is an iOS 15.0 app over a store, a keyed list and a query; docs/IOS_15_16.md says how CI proves the floor.

Edit this page on GitHub