Presenting alert in SwiftUI at the right time

As you might know, UIKit can't display alerts on top of each other. I'm having trouble writing SwiftUI application in WatchKit with a button, alert and action sheet.


### Sequence of events

1. Human taps the button

2. Action sheet appears with action confirmation

3. Human taps OK in the action sheet

4. Action block called which can return either success or error

5. In case of error, app needs to present an alert with its description



#### Expected

Alert with error description presented on top of a button


#### Current

> Warning: Attempt to present <PUICAlertSheetController: 0x823cbe00> on <_TtGC7SwiftUI19UIHostingControllerVS_7AnyView_: 0x80364150> whose view is not in the window hierarchy!


struct Favorits: View {

    @State private var isConfirmSheetPresent = false
    @State private var isAlertPresent = false
    @State private var error: String?
   
    var body: some View {
        List {
            Section(header: Text("Header")) {
                ForEach(self.dataSource) { drink in
                    DrinkCell(drink: drink)
                    .onTapGesture {
                        self.selectedDrink = drink
                        self.isConfirmSheetPresent = true
                    }
                }
            }
        }
        .alert(isPresented: $isAlertPresent) {
            Alert(title: Text(error!), message: nil, dismissButton: nil)
        }
        .actionSheet(isPresented: $isConfirmSheetPresent) {
            ActionSheet(title: Text(""),
                        message: nil,
                        buttons: [.default(Text(""), action: { // Run action block })])
        }
    }
}


Action block

Model.sharedInstance.run() { (success, error) in
  if success {
       // Update UI
  } else {
       // There's no way to know when confirmation alert is present
       // This won't work without a delay
       self.error = error!.localizedDescription
       self.isAlertPresent = true
  }
}


### Solutions

I can delay alert and it fixes the issue, but I don't believe this is the right approach. Ideally I need to know exactly when action sheet is dismissed.


I also have tried this, but isConfirmSheetPresent set to false while dismiss animation is still running.


while !isConfirmSheetPresent {
    // Display alert
}

Replies

Writing this question gave me a realisation how to solve it. Instead of running action block immediately from the confirmation sheet, run it in onAppear block in the main view.

Update

I have tried this approach and it did not work, it seams onApper is called too early and I get the same error. 😕

I have not found a solution yet, so instead I just changed UX of my app to use another tap confirmation mechanism rather than ActionSheet, it works even better now.
I ran into the same issue recently and solved it by moving the alert and sheet modifiers to different Views in the view hierarchy.
If you moved the alert or sheet to Section - it should fix the issue. Don't really understand the logic behind it but it works.

It may be failing because the parent view's contents aren't embedded in a NavigationView. If you put the entire contents inside a NavigationView it'll probably work. I just encountered this when trying to raise a sheet from a sheet that didn't have a NavigationView.

Interestingly, it failed on iOS 15 but not later.