swiftuialerttabview

Error on second tab alert Presenting view controller <SwiftUI.PlatformAlertController: > 0x10580c400> from detached view controller


Is this a wrong usage of the concept of "error manager"? Is it something in SwiftUI that is not used anymore? On second tab, when I try to show error, on console I get this:

Presenting view controller <SwiftUI.PlatformAlertController: 0x10580c400> from detached view controller <TtGC7SwiftUI19UIHostingControllerVS_14_ViewList_View: 0x10385ee00> is not supported, and may result in incorrect safe area insets and a corrupt root presentation. Make sure <TtGC7SwiftUI19UIHostingControllerVS_14_ViewList_View: 0x10385ee00> is in the view controller hierarchy before presenting from it. Will become a hard exception in a future release.

class ErrorManager: ObservableObject {
    static let shared = ErrorManager()
    @Published var currentError: TestError?
    
    private init() {}
    
    func handleError(_ error: TestError) {
        self.currentError = error
    }
}

enum TestError: Error, Identifiable, LocalizedError {
    var id: String {localizedDescription}
    
    case genericError
    
    var errorDescription: String? {
        switch self {
        case .genericError:
            return "Generic Error"
        }
    }
}


import SwiftUI

@main
struct errorManagerInTabViewControlelrAppApp: App {
    let classFromEntryPoint = ClassFromEntryPoint()
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(ErrorManager.shared)
                .environmentObject(classFromEntryPoint)
        }
    }
}

//*****************************************************

class ClassFromEntryPoint: ObservableObject {
    @Published var alertError: TestError?
    
    init() {}
    
    func testThrowsError() throws {
        print("func called")
        throw TestError.genericError
    }
}

//*****************************************************

import SwiftUI

struct ContentView: View {
    @State private var selectedTab: Int = 0
    
    var body: some View {
        TabView(selection: $selectedTab) {
            FirstView(changeTab: self.changeTab)
                .tabItem { Label("First", systemImage: "star") }
                .tag(0)
            SecondView()
                .tabItem { Label("Second", systemImage: "star") }
                .tag(1)
        }
    }
    
    func changeTab(to index: Int) {
        selectedTab = index
    }
}


//*****************************************************

import SwiftUI

struct FirstView: View {
    @EnvironmentObject var classFromEntryPoint: ClassFromEntryPoint
    @EnvironmentObject var errorManager: ErrorManager
    
    var changeTab: (Int) -> Void
    
    var body: some View {
        VStack {
            Text("Hello first")
                .alert(item: $errorManager.currentError) { error in
                    Alert(title: Text("Error"), message: Text(error.localizedDescription), dismissButton: .default(Text("OK"), action: {
                        // do stuff
                    }))
                }
            
            Button("launch the error") {
                do {
                    try self.classFromEntryPoint.testThrowsError()
                } catch {
                    ErrorManager.shared.handleError(error as! TestError)
                    print("Error triggered:  \(error.localizedDescription)")
                }
            }
        }
    }
}

//*****************************************************

import SwiftUI

struct SecondView: View {
    @EnvironmentObject var classFromEntryPoint: ClassFromEntryPoint
    @EnvironmentObject var errorManager: ErrorManager

    var body: some View {
        VStack {
            Text("Hello second")
            Button("Test Print") {
                do {
                    try self.classFromEntryPoint.testThrowsError()
                } catch {
                    ErrorManager.shared.handleError(error as! TestError)
                    print("Error triggered:  \(error.localizedDescription)")
                }
            }
        }
    }
}

Solution

  • Put the alert on the TabView or even ContentView not the FirstView.