skip to Main Content

I want to create a global variable for showing a loadingView, I tried lots of different ways but could not figure out how to. I need to be able to access this variable across the entire application and update the MotherView file when I change the boolean for the singleton.

struct MotherView: View {
    
    @StateObject var viewRouter = ViewRouter()
        
    var body: some View {
        
        if isLoading { //isLoading needs to be on a singleton instance
            Loading()
        }

        
        switch viewRouter.currentPage {
        case .page1:
            ContentView()
        case .page2:
            PostList()
        }
    }
}

struct MotherView_Previews: PreviewProvider {
    static var previews: some View {
        MotherView(viewRouter: ViewRouter())
    }
}

I have tried the below singleton but it does not let me update the shared instance? How do I update a singleton instance?

    struct LoadingSingleton {
    static let shared = LoadingSingleton()
    var isLoading = false

    private init() { }
}

2

Answers


  1. Make your singleton a ObservableObject with @Published properties:

    struct ContentView: View {
        @StateObject var loading = LoadingSingleton.shared
        
        var body: some View {
            if loading.isLoading {
                Text("Loading...")
            }
            ChildView()
            Button(action: { loading.isLoading.toggle() }) {
                Text("Toggle loading")
            }
        }
    }
    
    struct ChildView : View {
        @StateObject var loading = LoadingSingleton.shared
    
        var body: some View {
            if loading.isLoading {
                Text("Child is loading")
            }
        }
    }
    
    class LoadingSingleton : ObservableObject {
        static let shared = LoadingSingleton()
        @Published var isLoading = false
        
        private init() { }
    }
    

    I should mention that in SwiftUI, it’s common to use .environmentObject to pass a dependency through the view hierarchy rather than using a singleton — it might be worth looking into.

    Login or Signup to reply.
  2. First, make LoadingSingleton a class that adheres to the ObservableObject protocol. Use the @Published property wrapper on isLoading so that your SwiftUI views update when it’s changed.

    class LoadingSingleton: ObservableObject {
        @Published var isLoading = false
    }
    

    Then, put LoadingSingleton in your SceneDelegate and hook it into your SwiftUI views via environmentObject():

    class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    
        var window: UIWindow?
        static let singleton = LoadingSingleton()
    
        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            let contentView = ContentView()
    
            if let windowScene = scene as? UIWindowScene {
                let window = UIWindow(windowScene: windowScene)
                window.rootViewController = UIHostingController(rootView: contentView.environmentObject(SceneDelegate.singleton))
                self.window = window
                window.makeKeyAndVisible()
            }
        }
    }
    

    To enable your SwiftUI views to update when changing isLoading, declare a variable in the view’s struct, like this:

    struct MyView: View {
        @EnvironmentObject var singleton: LoadingSingleton
    
        var body: some View {
            //Do something with singleton.isLoading
        }
    }
    

    When you want to change the value of isLoading, just access it via SceneDelegate.singleton.isLoading, or, inside a SwiftUI view, via singleton.isLoading.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search