SwiftUI randomly crashes on presentationMode?.wrappedValue.dismiss()
Asked Answered
B

1

9

This is how the crash looks like

enter image description here

So it randomly crashes on the UIKit line

UIKitCore
-[UIViewController _ancestorViewControllerOfClass:allowModalParent:] + 44

I have View in default SwiftUI navigation stack:

struct MyView: View {
  @EnvironmentObject var viewModel: MyViewModel
  @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

var body: some View {
    ZStack {
      ......
    }
    .onAppear {
      self.viewModel
        .onViewAppear(presentationMode: self.presentationMode)
    }
  }
}

final class MyViewModel {
  var presentationMode: Binding<PresentationMode>?

  func onViewAppear(presentationMode: Binding<PresentationMode>) {
    self.presentationMode = presentationMode
  }

  func hide() {
    presentationMode?.wrappedValue.dismiss() // crashes after calling this
  }
}

So I push MyView in navigation stack this way:

NavigationLink(
      destination: MyView()
    ) {
      Image(systemName: "plus.circle")
        .font(.title)
    }

And then after user presses a button in MyView after several seconds I call hide() in the MyViewModel. Almost all the time it works but in 5-10% cases it crashes.

Brainsick answered 18/7, 2020 at 14:43 Comment(15)
Not sure what button you mean, but actually in MyViewModel I just load data from backend, display it and have DispatchQueue.main.asyncAfter(.now + 1) { self.hide() }Brainsick
"after user presses a button in MyView - this button" as I told in a comment above. After pressing that button I just call a viewModel.buttonPressed(). Then view model loads this data from backend and then on the main queue ViewModels calls hideBrainsick
" Why don't you call presentationMode?.wrappedValue.dismiss() directly in view? " Because I decide that I need to hide the view in the view model, because I need to hide the view after I got response from backend.Brainsick
"This transfer via view model looks strange for me" - it's a responsibility of a view model to hide the screen. Not the view. "If it happens that view is recreated (due to some parent refresh) then binding in view model might become dangling" - if view is recreated, the view model will be recreated too, because view is created with a Builder that builds a view model tooBrainsick
Do you need to add MyView().environmentObject(viewModel)? Seems like the view model wouldn't be set in that initialiser.Responser
Is it possible that the user closes the modal before you automatically hide it?Responser
@PranavKasetti in will be set. Even more, before hiding this view model start network request after View Appear, so 100% it is initializedBrainsick
@PranavKasetti nope, it crashes without any actions from User. User just waitsBrainsick
how do you know that? the user could be clicking the back button on the nav bar!Responser
@PranavKasetti I know because I can see the crash on my device, I can reproduce it from time to timeBrainsick
Ok. Just for testing, do you get the same crash if you use @StateObject instead of @EnvironmentObject? Maybe worth a try, this definitely seems like a SwiftUI bug though!Responser
@PranavKasetti I have never used StateObject , could you please give an example instead of Environment(\.presentationMode) ?Brainsick
Change @EnvironmentObject var viewModel: MyViewModel to @StateObject var viewModel: MyViewModel = MyViewModel()Responser
sure, I'll try and write back here, but in my opinion the issue is with @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode> somehowBrainsick
Any news about this? I'm also having this problem @PaulT.Solutrean
F
1

Fix for me was to set .navigationViewStyle(StackNavigationViewStyle())

NavigationView { content }.navigationViewStyle(StackNavigationViewStyle())

Febrifuge answered 17/9, 2020 at 7:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.