SwiftUI View Extension is called on View load/refresh

741 Views Asked by At

We created a custom view extension to extend the functionality of .alert and add a bunch of customizations to fit with our needs;

import SwiftUI

extension View {
    
    public func alertX(isPresented: Binding<Bool>, content: () -> AlertX) -> some View {
        
        let alertX_view = AlertX_View(visible: isPresented, alertX: content())
        let alertXVC = AlertXViewController(alertX_view: alertX_view, isPresented: isPresented)
        alertXVC.modalPresentationStyle = .overCurrentContext
        alertXVC.view.backgroundColor = UIColor.clear
        alertXVC.modalTransitionStyle = .crossDissolve
        
        if isPresented.wrappedValue == true {
            if AlertX_View.currentAlertXVCReference == nil {
                AlertX_View.currentAlertXVCReference = alertXVC
            }
            
            let viewController = self.topViewController()
            viewController?.present(alertXVC, animated: true, completion: nil)
        } else {
            alertXVC.dismiss(animated: true, completion: nil)
        }
        
        return self
    }
    ... truncated for brevity
}

It is called on a view in the same way as .alert is called;

        .alertX(isPresented: $viewModel.showAlert) {
            AlertX(logo: networkStore.currentNetwork.networkTheme.systemLogo ,
                   logoColor: networkStore.activeColors.lightTextColor ,
                   backgroundColor: networkStore.activeColors.primaryColor,
                   title: Text("AlertX Test"))
        }

Where $viewModel.showAlert is @Published var showAlert: Bool and AlertX is a custom view that contains our proprietary customizations to fit with our B2B application.

The issue I am having is that the information inside the closure for .alertX(isPresented: is called every time the view loads or changes state, regardless of whether or not the $viewModel.showAlert binding changes. The same is NOT true of the built in .alert view extension which is ONLY called when the $viewModel.showAlert value changes.

What modifications do I need to make in my implementation of public func alertX(isPresented: Binding<Bool>, content: () -> AlertX) -> some View { so that the information inside the closure it is only called when the binding value changes?

1

There are 1 best solutions below

1
On

Remember that SwiftUI will reload all of the structs and functions all the time - and then decides, based on the data dependencies, which part to redraw. So any code in your view functions will run. A lot.

Try this: at the top of that function, put

guard isPresented.wrappedValue == true else {
    return
}

Instead of checking it later on in the function.

Related, you might also want to look into making this code into a ViewModifier, and then your alertX function just applies the viewModifier based on the isPresented binding, otherwise returns self.