Swift Observability for Ferrostar 1.x #743
Replies: 3 comments 3 replies
-
|
This looks like a great summary! I'd agree with almost all points. Personally, I do use This summary mentions that both techs can live together. That was my very first thought. Personally, I'd totally see I currently don’t really understand btw,
I do believe you can initialize your Combine-based class as a service and later pass it into SwiftUI views where you’d use |
Beta Was this translation helpful? Give feedback.
-
|
@devnull133 & @ianthetechie thoughts on bumping to iOS 17+ so we can leverage ReasoningAfter working through similar issues on multiple projects I found the key reason was dropping If there are no major objections, I'll get started on a MR that aims to align everything architecturally that we can use to finalize this RFC. |
Beta Was this translation helpful? Give feedback.
-
|
This should help facilitate things. It's includes a shim that lets us extract |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Problem
In Kotlin/Android there's a single, well supported solution for observability with Kotlin Flows. On iOS with Swift, there are several solutions, each with advantages and drawbacks. These include Combine, SwiftUI ObservableObject, SwiftUI
@Observableand AsyncSequence.SwiftUI
@Published&ObservableObjectCombine is a feature rich solution and backs the
@Publishedproperty wrapper used with SwiftUI's ObservableObject. This is an effective tool to trigger SwiftUI updates while also enabling combine sink usage. However, it has the following drawbacks:@StateObjector@ObservedObject. This makes improper usage possible with more advance architectures where you might create it as a service. This is especially true when multiple view ports might need access to the same ferrostar core instance, e.g. CarPlay.@Publishedis passed through a protocol, it's not longer published nor observable. This can result in unexpected behavior when a developer uses an ObservableObject's@Publishedproperty in a SwiftUI view, but doesn't realize it's lost observability somewhere in the code stack.Basic Combine
Basic Combine subjects and publishers solve some of the potential misconfigurations with SwiftUI's ObservableObject through a more basic typed Swift approach with
CurrentValueSubjectandAnyPublisher. They don't rely on a property wrapper, enabling a protocol safe way to hold the source data and publish observable data streams through an app. The drawbacks with standard combine are the code is verbose, can't be directly consumed in a SwiftUI view, and integrates older swift techniques and tools. All combine subjects and publishers provide a.valuesAsyncSequence type that allows direct use in modern concurrency contexts. However, its larger scope of legacy Swift tooling means there are several ways to solve the same problems. This increases reviewer workload and the potential for code variance, bugs, etc.SwiftUI
@ObservableThe
@Observablemacro is a great solution for observable values that are used in SwiftUI views. It's automation is simple, efficient and effective. The primary drawback is the lightweight implementation in iOS versions before 26.0. In our existing Ferrostar environment, this means it's extremely difficult to consume an@Observableparameter outside of a SwiftUI view. In Swift 6.2 (os 26.0) several new features are available for this API based on this proposal SE-0475.This is the best solution, but for now not possible while supporting our current minimum deployment targets.
AsyncSequences
Lastly comes AsyncSequences (the AsyncStream and AsyncThrowingStream). AsyncSequences are a first class modern concurrency tool that makes usage of a stream extremely safe, with compiler safety checks to enforce standard usage. Most modern API's are adopting this as the primary tool for publishing data over time. E.g. CoreLocation's newer
CLLocationUpdate.liveUpdates().The primary issue with an AsyncSequence, is that the system lacks a true data source that can be listened to by multiple consumers. Several solutions to this are coming forward, but many are also guarded by newer Swift 6.2 and iOS 26 availability flags. E.g:
AsyncSequence.share()for sharing a sequence across multiple consumers.Recommended Approach
The ideal outcome is
@Observablewith the AsyncStream-likeObservationsbehavior in SE-0475. However, with our minimum deployment targets, we won't have reasonable access to the fullObservableframework until a year or two from now. Without an aggressive minimum update to iOS 26.For now, we could adopt AsyncSequences for FerrostarCore's data streams. This gives us a straightforward and safe way for developers to consume updates in a modern concurrency environment. It'll allow us to drop SwiftUI imports for FerrostarCore's target and focus UI behaviors in the UI targets.
A developer can then safely use the state as a service in any Swift
Task. They can also safely consume it using SwiftUI's task modifier.Future Enhancements
This solution doesn't force us to abandon SwiftUI's
@Observablemacro. It just means we'll have to move it to a SwiftUI focused wrapper for ferrostar core. This allows us to support multiple SwiftUI wrappers for different versions of iOS. Allowing developers the flexibility to initialize ferrostar core as a simple Swift service, or directly in a SwiftUI view with a specialized wrapper.Beta Was this translation helpful? Give feedback.
All reactions