swiftui-router's People
swiftui-router's Issues
Handling model state bindings
The demo showcases ViewModels that keep a separate, local state (isLoggingIn
) or passing on methods to services:
class AccountScreenViewModel: BaseViewModel<Services>, ObservableObject {
func logout() {
self.services.loginManager.logOut()
}
}
But what about when we have UI that requires a binding directly to the state? A TextField, List selection or Toggle for example.
If we imagine that we have a UserManager that has a state for user status:
public protocol UserManager {
var userStatus: UserStatus { get }
}
public class UserStatus: ObservableObject {
@Published var isAway: Bool = false
}
And a view for the User with a toggle for their status:
struct UserScreen: View {
@State var router: UserScreenRouter
@StateObject var viewModel: UserScreenViewModel
var body: some View {
Toggle("Away", isOn: <Binding<Bool>>)
}
}
And a view model:
class UserScreenViewModel: BaseViewModel<Services>, ObservableObject {
}
What should we put in the ViewModel here? Where does the binding go?
We can't bind it directly to viewModel.services.userManager.userStatus.isAway, and even if we could it would not guarantee that the view will refresh properly.
Do we create an intermediary state in the ViewModel that we bind to the original state? I can see it being done like this:
class UserScreenViewModel: BaseViewModel<Services>, ObservableObject {
@Published var isAway: Bool = false {
didSet {
guard oldValue != isAway else { return }
services.loginManager.state.isAway = isAway
}
}
override init(services: Services) {
super.init(services: services)
services.loginManager.state.$isAway.removeDuplicates().assign(to: &$isAway)
}
}
But it is an annoying bit of setup for what is a very common use case. Have you figured out a better solution?
Question about new Dependency Injection mechanism
I went through the update to the blog post and project, but I'm confused about the new "dependency injection" mechanism via property wrappers.
The new solution to injecting dependencies is to use a new key wrapper that defines access to dependencies:
Dependencies are brought into ViewModels or Routers by using the @injected property wrapper along with the keypath for the dependency. e.g: @injected(.loginManager) var loginManager will bring the loginManager loaded in DependencyInjection.assembly into a view model.
But isn't this literally just making the previous AppServices
a singleton? It doesn't look like dependency injection at all to me. You have to hardcode all the available dependencies in advance into DependencyInjection
, which then just provides global access to them via a keyPath.
Looking at LoginScreenViewModel
for example, it's using:
@Injected(\.loginManager) var loginManager
But it could just as well be using
var loginManager2 = DependencyInjection.assembly.loginManager
It works exactly the same.
There doesn't seem to be any gain here over just keeping the old AppServices and making it a singleton... Or am I missing something?
Templates for Mac OS projects
Make the included file templates available for Mac OS projects (targets) as well
Considerations for @StateObject and App declarations
Hi! I've been searching for a scalable architecture for SwiftUI projects for a while now and I'm very impressed by this pattern so far. I'm working on implementing it into a new project, and there are some thoughts that I had while working with it:
Many router views declare their router variable as @StateObject
, which implies that "SwiftUI creates a new instance of the object only once for each instance of the structure that declares the object". But when we pass this object as a parameter:
@ViewBuilder func loginScreen() -> some View {
LoginRouterView(router: LoginRouter(services: self.services))
}
Is there a point to keeping it declared as @StateObject
as opposed to @ObservedObject
? It was my understanding that the @StateObject
property wrapper is only relevant when instantiating the object inside of the view struct.
In general, I'd also consider it a smell to initiate objects in a view body/builder like this. These objects used to be declared as lazy variables on the parent router, and that code is still there but no longer used:
Did you reach a new conclusion here that caused you to make this change?
Additionally, for the App struct I would propose to move from:
struct RouterTestApp: App {
var body: some Scene {
WindowGroup {
AppRouterView(router: AppRouter(services: AppServices()))
}
}
}
To
struct RouterTestApp: App {
@StateObject var router = AppRouter(services: AppServices())
var body: some Scene {
WindowGroup {
AppRouterView(router: router)
}
}
}
To avoid creating the object in the body, and also enable the AppServices
to be instantiated as early as possible in the App lifecycle.
Link to blog post
Loving this and keep coming back to it. Would it be possible to add a link to your blog post about it in the readme for reference?
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. ๐๐๐
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google โค๏ธ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.