Search code examples
swiftswiftuiviewbuilder

How to use BaseView @State property in SubView SwiftUI


I have a BaseView something like this and I want to add alert mechanism to all my subviews

struct BaseView<Content: View>: View {
    @State var isAlertPresented = false
    let content: Content
    
    init(@ViewBuilder content: () -> Content) {
        self.content = content()
    }
    
    var body : some View {
        content.alert(isPresented: $isAlertPresented) {
            Alert(title: Text("title"))
        }
    }

}

And here is my SubView.

struct SubView: View {
    BaseView {
        Text("")
    }.onReceive(vm.publisher) { (output) in
        // here I want to trigger BaseView isAlertPresented property        
        // BaseView.isAlertPresented = true
    }
}

Can I do something like this? If yes how can I?


Solution

  • If view contains BaseView it is definitely not a subview relating to it. And you should not access/manipulate internal state of other view due to single-source-of-truth violation.

    Instead you have to use Binding in this scenario, like below (tested with Xcode 11.7)

    struct BaseView<Content: View>: View {
        @Binding var isAlertPresented: Bool
        let content: Content
    
        init(isAlertPresented: Binding<Bool>, @ViewBuilder content: () -> Content) {
            self._isAlertPresented = isAlertPresented
            self.content = content()
        }
    
        var body : some View {
            content.alert(isPresented: $isAlertPresented) {
                Alert(title: Text("title"))
            }
        }
    
    }
    
    struct SomeOtherView: View {
        @State private var showAlert = false
    
        var body: some View {
            BaseView(isAlertPresented: $showAlert) {
                Text("Test")
            }
            .onReceive(vm.publisher) { (output) in
    //        .onAppear {     // used instead of above just for test
                self.showAlert = true
            }
        }
    }