Giter VIP home page Giter VIP logo

swiftui-router's People

Contributors

davidgarywood avatar davidwsnapper avatar joadan990 avatar timcolson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

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?

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:

https://github.com/roboheadz/swiftui-router/blob/2cddc8c4b186cac818bcad56a44794417cbe2235/swiftui-router/Routers/AppRouter.swift#L26

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 photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.