How to pass a SwiftUI View into a class as a property? (AlertPresenter)

190 Views Asked by At

I am trying to create an alert presenter to control alerts using SwiftUI.

The goal is to use a simple top level function to present an alert.


The following is my code:

First, create an AlertPresenter which has a present and dismiss alert function.

class AlertPresenter: ObservableObject{
 
    @Published var tittle: String
    @Published var isPresented: Bool
    @Published var action: //Issue: I want to pass a view in this property, a optional View will be better
    @Published var message: //Issue: I want to pass a view in this property

   
    init() {
        self.tittle = ""
        self.isPresented = false
        self.action = EmptyView()
        self.message = EmptyView()
    }


    //Issue: I want to pass SwiftUI View as a function parameter
    func present(tittle: String, message: String, action: @escaping () -> View, message: @escaping () -> View){
        self.tittle = tittle
        self.isPresented = true
        self.action = action
        self.message = message
    
    }

    func dismiss(){
        self.tittle = ""
        self.isPresented = false
        self.action = EmptyView()
        self.message = EmptyView()
    }
}

We create the AlertPresenter, and pass it as an EnvironmentObject, and then connect the AlertPresenter with the parent view

struct ParentView: View{

    @EnvironmentObject var alertPresenter: AlertPresenter = AlertPresenter()

    var body: some View{
        ChildView()
            .environmentObject(alertPresenter)
            .alert(tittle: alertPresenter.tittle, isPresented: $alertPresenter.isPresented){
                alertPresenter.action
            } message: {    
                alertPresenter.message    
            } //this function is alert(_:isPresented:actions:message:) available from iOS15.0+
            
    }

}

At the end, I want to use a simple top level function to present and dismiss an alert:

Button("Present Alert"){
    alertPresenter.present(tittle: "This is the alert tittle"){
        Button("OK"){
            //dismiss alert
            alertPresenter.dismiss()
        }
    } message: {
        Text("This is the alert message")
    }
}


Thanks :)

1

There are 1 best solutions below

0
On

What you CAN do is reduce the inputs to strings to display (which obviously reduces the flexibility):

class AlertPresenter: ObservableObject {
 
    @Published var title: String
    @Published var isPresented: Bool
    @Published var buttonTitle: String
    @Published var message: String

   
    init() {
        self.title = ""
        self.isPresented = false
        self.buttonTitle = "OK"
        self.message =  ""
    }


    func present(title: String, buttonTitle: String, message: String) {
        self.title = title
        self.isPresented = true
        self.buttonTitle = buttonTitle
        self.message = message
    
    }

    func dismiss(){
        self.title = ""
        self.isPresented = false
        self.buttonTitle = "OK"
        self.message =  ""
    }
}




struct ContentView: View {
    
    @StateObject var alertPresenter = AlertPresenter()

    var body: some View{
        ChildView()
            .environmentObject(alertPresenter)
            .alert(alertPresenter.title, isPresented: $alertPresenter.isPresented) {
                Button(alertPresenter.buttonTitle) { alertPresenter.dismiss() }
            } message: {
                Text(alertPresenter.message)
            } //this function is alert(_:isPresented:actions:message:) available from iOS15.0+
    }
}

struct ChildView: View {
    
    @EnvironmentObject var alertPresenter: AlertPresenter

    var body: some View{
        Button("Present Alert"){
            alertPresenter.present(title: "This is the alert title", buttonTitle: "Continue", message: "This is the alert message")
        }
    }
}