Search code examples
xcodeswiftuiobservedobject

SwiftUI @StateObject couldn't refresh view


I can't see the change in the Text object on the ContentView page. However, when I run the same code in .onReceive with print, I can see the change. What's the problem here?

I wanted to manage the state of the game from a different place and the operation of the game from a different place. Is the logic I made wrong?

Enum

enum GameSituation {
    case play
}

Game Config

class GameConfig: ObservableObject {
    @Published var randomCellValue: Int = 0
    @Published var timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
    
    func startGame() {
        determineRandomCell()
    }
    
    func determineRandomCell() {
        randomCellValue = Int.random(in: 0...11)
    }

    func playSound(soundfile: String, ofType: String) {
        
        if let path = Bundle.main.path(forResource: soundfile, ofType: ofType){
            do{
                audioPlayer = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
                audioPlayer.prepareToPlay()
                audioPlayer.play()
            } catch {
                print("Error")
            }
        }
    }
}

Game Situation

class GameSituations: ObservableObject {
    @Published var gameConfig = GameConfig()
    
    func gameSituation(gameSituation: GameSituation) {
        switch gameSituation {
        case .play:
            gameConfig.startGame()
        }
    }
}

Content View

struct ContentView: View {
    @StateObject var gameSituation = GameSituations()
    var body: some View {
        Text("\(gameSituation.gameConfig.randomCellValue)")
            .padding()
            .onReceive(gameSituation.gameConfig.timer, perform: { _ in
                gameSituation.gameSituation(gameSituation: .play)
                print("random: \(gameSituation.gameConfig.randomCellValue)")
            })
    }
}

Solution

  • You had multi issue I solved all, your first and big issue was, that you are initializing an instance of an ObservableObject but using another one! second one was that you forgot using StateObject of GameConfig in your View, as you can see in your code, you did used StateObject of GameSituations but not GameConfig, if you ask why, because it is Publisher!

    My best recommendation: try just use one ObservableObject! using 2 deferent ObservableObject that has binding with together has such a issue that you can see! try organise all your code to one ObservableObject.


    enum GameSituation {
        case play
    }
    

    class GameConfig: ObservableObject {
        
        static let shared: GameConfig = GameConfig()    // <<: Here
    
        @Published var randomCellValue: Int = 0
        @Published var timer = Timer.publish(every: 2, on: .main, in: .common).autoconnect()
        
        func startGame() {
            determineRandomCell()
        }
        
        func determineRandomCell() {
            randomCellValue = Int.random(in: 0...11)
        }
    
        func playSound(soundfile: String, ofType: String) {
            
            print(soundfile)
    
    
        }
    }
    

    class GameSituations: ObservableObject {
        
        static let shared: GameSituations = GameSituations()  // <<: Here
    
        let gameConfig = GameConfig.shared        // <<: Here
         
        func gameSituation(gameSituation: GameSituation) {
            switch gameSituation {
            case .play:
                gameConfig.startGame()
            }
        }
    }
    

    struct ContentView: View {
        
        @StateObject var gameSituation = GameSituations.shared     // <<: Here
        @StateObject var gameConfig = GameConfig.shared            // <<: Here
        
        var body: some View {
            
            Text(gameConfig.randomCellValue.description)          // <<: Here
                .onReceive(gameConfig.timer, perform: { _ in      // <<: Here
    
                           gameSituation.gameSituation(gameSituation: .play) 
                            print("random: \(gameSituation.gameConfig.randomCellValue)")
            
                        })
    
        }
    }