chuk
chuk

Reputation: 79

SwiftUI: Singleton class not updating view

New to SwiftUI... I have the following simplified code. The intended functionality is to be able to navigate between View1() and View2(), using a singleton class to keep track of this navigation.

I suspect that maybe I needed to add @Published to my show_view_2 variable, but I want to keep it in App Storage. Also, I understand this isn't the best way to switch between views, but I only am using this method because it's a minimally reproduced example.

Why isn't the below code working, and how can I make it work?

class Player {
    static var player = Player()
    @AppStorage("show_view_2") var show_view_2: Bool = false
}

struct ContentView: View {  
    var body: some View {
        if(Player.player.show_view_2) {
            View2()
        } else {
            Text("Go to View2")
                .onTapGesture {
                    Player.player.show_view_2 = true
                }
        }
    }
}

struct View2: View {
    var body: some View {
        Text("Back to View1")
            .onTapGesture {
                Player.player.show_view_2 = false
            }
    }
}

Upvotes: 1

Views: 1136

Answers (1)

jnpdx
jnpdx

Reputation: 52475

For SwiftUI to know to update, Player should be an ObservableObject. Also, it'll need to be accessed using a property wrapper (either @StateObject or @ObservedObject) on the View:

class Player : ObservableObject {
    static var player = Player()
    @AppStorage("show_view_2") var show_view_2: Bool = false
}

struct ContentView: View {
    @StateObject private var player = Player.player
    
    var body: some View {
        if player.show_view_2 {
            View2()
        } else {
            Text("Go to View2")
                .onTapGesture {
                    player.show_view_2 = true
                }
        }
    }
}

struct View2: View {
    @StateObject private var player = Player.player
    
    var body: some View {
        Text("Back to View1")
            .onTapGesture {
                player.show_view_2 = false
            }
    }
}

In general, I'd recommend not using a singleton and instead passing an instance of the object explicitly between views -- this will be more testable and flexible over time:

class Player : ObservableObject {
    @AppStorage("show_view_2") var show_view_2: Bool = false
}

struct ContentView: View {
    @StateObject private var player = Player()
    
    var body: some View {
        if player.show_view_2 {
            View2(player: player)
        } else {
            Text("Go to View2")
                .onTapGesture {
                    player.show_view_2 = true
                }
        }
    }
}

struct View2: View {
    @ObservedObject var player : Player
    
    var body: some View {
        Text("Back to View1")
            .onTapGesture {
                player.show_view_2 = false
            }
    }
}

Upvotes: 2

Related Questions