swiftswiftuiappdelegateuiscenedelegate

Swiftui redirect to another view from SceneDelegate


When a specific url is opened we enter to this function

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {

I need to go to a specific screen. What I tried to do is to keep the contentView instance and I tried to set index table to go to the other screen but it doesn't work because contentView instance isn't linked correctly.

Here is what I tried:

      class SceneDelegate: UIResponder, UIWindowSceneDelegate {
      var contentView: some View {
    let context = (UIApplication.shared.delegate as! 
    AppDelegate).persistentContainer.viewContext
    // Remplacez cette partie par votre initialisation de ContentView
   return ContentView().environment(\.managedObjectContext, context)
}...


  func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else {
        return
    }...
                let a = contentView
        if let contentView = (a as? ModifiedContent<ContentView, _EnvironmentKeyWritingModifier<NSManagedObjectContext>>)?.content {
            // Faire quelque chose avec la contentView ici
            contentView.indexTab = 2
        }

My ContentView:

struct ContentView: View {
    @State public var indexTab = 0 ...

body:

     var body: some View {
    VStack{
        if(indexTab > -1){
            
            TCTabBar(images: [
                Image(systemName: "house.fill"),
                Image(systemName: "cart.fill"),
                Image(systemName: "heart.fill"),
                Image(systemName: "gear")
            ], tabIndex: indexTab, contentTabs: [
                AnyView(ProductCatalog(products: $items, cart: $cart, favorites: $favorites)),
                AnyView(CartView(items: $cart)),
                AnyView(ProductCatalog(products: $favorites, cart: $cart, favorites: $favorites)),
                AnyView(SettingsView())
            ])
        }
    }.task {
        ContentViewModel.shared.contentView = self
    }

the problem is that ContentView never update from SceneDelegate.
How can I correct this problem?


Solution

  • You definitely don't want to reference contentView directly and modify it because the view can be recreated at any point.

    What you should do instead is create some kind of ViewModel or some other kind of object holding on to the global state (in this case, your indexTab). This object can then conform to the ObservableObject protocol and have @Published public var indexTab = 0 defined.

    You can initialise a single instance of this object in your SceneDelegate and pass it down to your view via @EnvironmentObject var stateStore: StateStore

    So you will end up with something like this.

    class StateStore: ObservableObject {
        @Published public var indexTab = 0
    }
    
    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
        
        var stateStore: StateStore = .init()
        
        var contentView: some View {
            return ContentView()
                ...
                .environmentObject(stateStore) // Pass it down via Environment
        }
        
        func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
            guard let url = URLContexts.first?.url else { return }
            ...
            stateStore.indexTab = 2
        }
    
        struct ContentView: View {
            @EnvironmentObject var stateStore: StateStore // This will traverse environment and find matching object based on type
            
            var body: some View {
                TCTabBar(
                    images: [ ... ],
                    tabIndex: stateStore.indexTab, // Use published value here
                    contentTabs: [ ... ]
                )
            }
        }
    }