1

While building my app I stumbled upon a bug that keeps me up at night. I have a View which has a button that when pressed is supposed to change value of a field of a StateObject which should activate a NavigationLink. Up until now app works as expected. However when I am in the View that navigationLink leads to and I put my app to background by quitting it and opening it again the navigation view pops back to root view. Also, I set up an onReceive to print when "isShowingFocusView" (the field which activates the NaigationLink) changes. When quitting it shows that value changes by itself to false thus deactivating navigationLink.

This is the view model which contains the field that is the core of my problem.

class OverlayButtonViewModel: ObservableObject {
@Published var isShowingFocusView: Bool

init() {
    isShowingFocusView = false
}
}

This is where I define the ViewModel and pass it as parameter to buttonView which pressed activates the NavigationLink.

struct OverlayButtonView: View {
@StateObject var viewModel = OverlayButtonViewModel()

// some view hierarchy

HStack {
              Spacer()
                    
              OverlayFocusButtonView(show: $viewModel.isShowingFocusView)
                    .offsetToPositionIfNot(self.showingButtons, self.homeLocation)
                    .opacityIfNot(self.showingButtons, 0)
                    .padding(.trailing, 45)
                    .onChange(of: viewModel.isShowingFocusView) { value in
                            print("Current value \(value)")
                        }
       }

Here is how I defined the view that consists the problematic NavigationLink:

struct OverlayFocusButtonView: View {
@Binding var show: Bool

var body: some View {

    NavigationLink(destination: FocusView(show: $show), isActive: $show) {
        Circle()
            .foregroundColor(Color("IdestBlue"))
            .frame(width: 60, height: 60)
            .overlay(Image("FocusModeIcon")
                        .resizable()
                        .renderingMode(.template)
                        .foregroundColor(.white)
                        .frame(width: 30, height: 30, alignment: .center)
            )
    }.onTapGesture {
        show = true
    }
}
}

Lastly, this is the part of the view where I deactivate the NavigationLink

struct FocusView: View {
@Binding var show: Bool

HStack{
           Image(systemName: "arrow.backward")
               .foregroundColor(.black)
               .font(.system(size: 20, weight: .bold))
               .padding(.leading, 20)
               .padding(.top, 10)
               .onTapGesture {
                   show = false
               }
           Spacer()
       }
       }

My understanding of the topic is that in order for app to not reinitiate field when reloading view it must be a State property and this is what I am doing, yet still the value change occurs.

To be clear: I expect that when I go back to my app I will see the same view as the one I saw when I was quitting.

Thank you for your help in advance. I really don't see what I am doing wrong here so if you have any idea what might be incorrect please help :)

Mikolaj
  • 56
  • 5
  • Does this answer your question? [SwiftUI: When I unlock apps it takes me back to the list](https://stackoverflow.com/questions/70266369/swiftui-when-i-unlock-apps-it-takes-me-back-to-the-list) – lorem ipsum Jan 03 '22 at 22:23
  • Thank you for your reply. I am trying to implement the solution you are suggesting however when using NavigationLink with tag/selection/destination/label parameters I am getting following error: Cannot convert value of type 'FocusView' to expected argument type '() -> Destination'. Do you know how can i fix it? – Mikolaj Jan 04 '22 at 00:06
  • Add curly brackets around FocusView – lorem ipsum Jan 04 '22 at 00:28
  • Now NavigationLink works just like before. It navigates to FocusView but when put to background it pops back to root of navigationView. However now when going to background SceneStorage field is set to nil not false. This is how I declared it: @SceneStorage("showFocus") var showFocus: Bool? and this is my new NVL: NavigationLink(tag: true, selection: $showFocus, destination: {FocusView(show: $showFocus)}) { ... } – Mikolaj Jan 04 '22 at 00:57
  • You likely have something resetting your view. Without a [Minimal Reproducible Example](https://stackoverflow.com/help/minimal-reproducible-example) it is impossible to help you troubleshoot. But there is an off documentation it is something like `self._print…` I don’t remember exactly and I’m not close to my Mac. You might be able to figure it out with that. Put it in the body. – lorem ipsum Jan 04 '22 at 02:00
  • After some trial and error I think I found a solution. On the main screen of my app I have a NavigationView with TabView inside. I believe the problem is connected with the fact that I used one NavigationView with multiple TabViews. When changing it so that every individual tabView contains their own NavigationView everything works as expected. Unfortunately, this solution has its own drawbacks ... Thank you for your help :) – Mikolaj Jan 04 '22 at 12:20

1 Answers1

1

The issue, I believe, is where you create the state object. You're creating the object when the view is initialized. However, SwiftUI may re-instantiate that object for certain reasons, like backgrounding the application.

Instead, I would hoist the creation of the @StateObject up into your {AppName}App.swift file and pass it in as an environment object there.

@main
struct MyApp: App {
    
    @StateObject var viewModel = OverlayButtonViewModel()

    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(viewModel)
        }
    }

}


struct OverlayButtonView: View {
    @EnvironmentObject var viewModel: OverlayButtonViewModel
    
    var body: some View {
        SomeContent($viewModel)
    }
}

Jake
  • 2,126
  • 1
  • 10
  • 23
  • More information can be found [here](https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app) – Jake Jan 03 '22 at 21:54
  • 1
    Thank you for your help. Unfortunately your suggestion does not fix my problem :( I checked and my app is not re-instatiating viewModel, it seems to just set the isShowingFocusView to false. – Mikolaj Jan 03 '22 at 22:32