Reputation: 323
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() { }
}
Upvotes: 2
Views: 9316
Reputation: 1918
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
.
Upvotes: 1
Reputation: 52505
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.
Upvotes: 11