UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: @Publishing Problem in SwiftUI - @Published Property Not Updating

Forums > SwiftUI

In the code below, part of a crossword app, I have created a Grid, which contains squares, an @Published array of Squares, and a GridView to display an instance of Grid, griddy. When I change griddy's squares in the GridView, the GridView gets recreated, as expected, and I see an "!" instead of an "A".

Now I've added one more level -- a Puzzle contains an @Published Grid, griddy. In PuzzleView, I therefore work with puzzle.griddy. This doesn't work, though. The letter doesn't change, and a break point in PuzzleView's body never gets hit.

Why? I'm working on an app where I really need these 3 distinct structures.


import SwiftUI

class Square : ObservableObject {
    @Published var letter: String

    init(letter: String){
        self.letter = letter
    }
}

class Grid : ObservableObject {
    @Published var squares:[[Square]] = [
        [Square(letter: "A"), Square(letter: "B"), Square(letter: "C")],
        [Square(letter: "D"), Square(letter: "E"), Square(letter: "F"), ]
    ]

}

class Puzzle: ObservableObject {
    @Published var griddy: Grid = Grid()
}

struct GridView: View {
    @EnvironmentObject var griddy: Grid

    var body: some View {
        VStack {
            Text("\(griddy.squares[0][0].letter)")
            Button("Change Numbers"){
                griddy.squares[0][0] = Square(letter:"!")
            }
        }
    }
}

struct PuzzleView: View {
    @EnvironmentObject var puzzle: Puzzle

    var body: some View {
        VStack {
            Text("\(puzzle.griddy.squares[0][0].letter)")
            Button("Change Numbers"){
                puzzle.griddy.squares[0][0] = Square(letter:"!")
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {

    static var previews: some View {
        // PuzzleView().environmentObject(Puzzle())
        GridView().environmentObject(Grid())
    }
}

3      

hi,

i admit to being confounded by these types of things as well, but i'll give it a try.

when you use GridView, the environment object is a Grid, which publishes any changes to its squares array. you make a direct change to the squares array in the grid, so it publishes the change and GridView can see that change.

when you use PuzzleView, the environment is a Puzzle that publishes any changes to its grid. you make a direct change to the squares array in the grid (which does, indeed, change), so it publishes the change and ... who can see that? who is listening? no view is subscribed to it.

this is where Combine comes in to play, so that a PuzzleView can subscribe to changes in the squares of the Grid. admittedly, i struggle with real Combine code; but the code below does solve the problem by making sure that when a Puzzle makes a change to one of the Squares of its Grid, the Puzzle broadcasts that change. (this is both a poor-man's avoidance of real Combine code, and not liking to reach deep inside an object with syntax such as puzzle.griddy.squares[0][0] = Square(letter:"!"))

class Square {
    var letter: String

    init(letter: String){
        self.letter = letter
    }
}

class Grid : ObservableObject {
    var squares:[[Square]] = [
        [Square(letter: "A"), Square(letter: "B"), Square(letter: "C")],
        [Square(letter: "D"), Square(letter: "E"), Square(letter: "F"), ]
    ]

    func updateSquare(row: Int, col: Int, newLetter: String) {
        squares[row][col].letter = newLetter
    }
}

class Puzzle: ObservableObject {
    var grid: Grid = Grid()

    func updateSquare(row: Int, col: Int, newLetter: String) {
        objectWillChange.send()
        grid.updateSquare(row: row, col: col, newLetter: newLetter)
    }

}

struct PuzzleView: View {
    @EnvironmentObject var puzzle: Puzzle

    var body: some View {
        VStack {
            Text("\(puzzle.grid.squares[0][0].letter)")
            Button("Change Numbers"){
                puzzle.updateSquare(row: 0, col: 0, newLetter: "!")
            }
        }
    }
}

hope that helps,

DMG

4      

Hacking with Swift is sponsored by RevenueCat.

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.