-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SwiftUI sample/boilerplate #654
base: main
Are you sure you want to change the base?
Conversation
Thanks for the contribution! Before we can merge this, we need @rickclephas to sign the Salesforce Inc. Contributor License Agreement. |
This is great! Going to take a deeper dive into the code in a bit, but in the meantime could you do three things for us?
|
Could you also merge latest main or articulate why some of the dependencies were lowered? Also any commented code bits |
Ah right forgot to mention that. Was having some issues building the iOS counter project due to the following change: add(NATIVE_COMPILER_PLUGIN_CLASSPATH_CONFIGURATION_NAME, libs.androidx.compose.compiler) Removing that just left me with some errors about incompatible Compose/Kotlin version, which is why I downgraded them. Will merge the latest main and take a look at your other questions 👍🏻. |
@ZacSweers I updated the description, let me know if there are other areas of which you would like some more details.
Not much has changed. I have added a local Swift package dependency to the sample project. |
Apologies for the delays, we haven't had much time to pick this up again yet. I'd be curious for your thoughts on SKIE though? The biggest annoyance I had in my own sample was getting UI state updates propagating to swift and it looks like this can help with that, possibly with molecule. |
No problem.
OMG that is genius! We have been thinking about this way to much from the Kotlin side. So short answer: I don't think you'll need SKIE (or any library/tool). Long answer:
However your comment made me realise we don't need anything like this. class CircuitPresenter: ObservableObject {
@Published var state: State
} That's it, we don't need a Flow, just a property that will notify subscribers of any changes. I think we can completely drop all external dependency (except for Molecule). SKIE could technically generate that for use, though not out-of-the-box. Will try and see if I can update the PR without external dependencies if I find some time 😁. |
Awesome! Yeah a dependency-less solution (other than molecule) would be ideal, and what you described in your snippet is exactly what I was thinking 👍 |
Updated the implementation to only depend on Molecule. Was even able to remove the extension on The |
Maybe it's even possible to move the "entry point" from the case let screen as IosCounterScreen:
CircuitView(screen.presenter(), CounterView.init) to something like: case let screen as IosCounterScreen:
screen.present(CounterView.init) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While this is technically really cool, does this really require rewriting much of circuit's runtime itself in Swift? That seems counter to the point of trying to share UI. The hope was to get to a point where one could write presentation logic in kotlin and circuit's existing APIs, and only the UI would be SwiftUI. This seems to be more akin to a pure swift reimplementation of core components
I am not really sure what you mean. When the goal is to use SwiftUI for the UI and Circuit for the presentation logic you'll need a way to tell SwiftUI about state changes and navigation events. In order to do that and provide that functionality as a library you'll need some way to connect the Circuit Kotlin code with the Circuit Swift code. |
I don't think we want to re-implement so much of this in Swift (±UI) to make it work there, my hope was that we would only need a thin bridge layer to convert |
@chrisbanes would be curious for your thoughts in this space as well since you've been using some of circuit's stuff on iOS already |
My direct experience with Circuit on iOS is fully within Compose, however I have spent a while recently hooking up Decompose + 'native UI' at work, and this has ended up looking very similar. I guess there's a bigger decision here: does using Circuit make much sense without Compose UI? 🤔 By using Swift UI (or even Android Views) you lose a lot of benefits from having a single composition, consistent navigation system, cross-cutting concept (CompositionLocals), and much more. My $0.02 would be that Circuit should be Compose only, and make that as good as it can be. There are other libraries out there which are focused on the more general x-platform problem. |
Correct. Although most of it is just glue code.
Alright. In that case I am wondering what responsibilities you were seeing for SwiftUI?
They add navigation support, allowing you to trigger navigation actions from the presenter.
That is indeed a great question. To be honest, I am not sure if it does make much sense. |
I know you are not really accepting contributions, so feel free to close this, just wanted to demonstrate an approach to use Circuit with SwiftUI.
Highlights
NavigationStack
Demo
I have added a
PrimeView
to the iOS sample similar to the desktop sample.Otherwise the UI hasn't changed much (git isn't smart enough to detect this after the file rename..).
Simulator.Screen.Recording.-.iPhone.14.Pro.-.2023-05-27.at.20.58.25.mp4
Components
There are a couple main components required to make this work.
SwiftUIPresenter
It's a small wrapper around an existing
Presenter
and is the main link between Kotlin and Swift.SwiftUIPresenter
exposes a function to set a listener that is used to notify Swift of any state changes.Besides exposing the state to SwiftUI it also provides a way for SwiftUI to store a navigator and cancel the coroutine scope.
SwiftUINavigator
/CircuitNavigator
Which brings us to
SwiftUINavigator
. It forwards navigation actions to a Swift navigator.To accomplish that it relies on a ObjC protocol that is implemented in the Swift part of the library.
CircuitPresenter
Is a helper protocol. It allows us to rely on our Kotlin code. User just need to add the following line to their iOS project:
CircuitNavigationStack
It is a wrapper around
NavigationStack
.You provide it with a
CircuitNavigator
and a closure that generates aCircuitView
based on the provided screen.It also stores the
CircuitNavigator
as an environment object such thatCircuitView
s can access it.CircuitView
This view connects a presenter to a SwiftUI view. It observes the
SwiftUIPresenter
and forwards the navigator to it.Usage
So how do you use this in an iOS project? First you need to add the above mentioned extensions.
After that you'll go ahead and create your views. There is nothing special about those. They are just SwiftUI views accepting a state value:
The only thing remaining is to connect the presenters to their view and setup the navigator: