GrandSteph
GrandSteph

Reputation: 2271

Preview issue with var initialized from environment variable

I'm having an issue in preview mode only because of the way I initialise one of my variables. This view takes a PlayerScore as parameter because I need to know which score to edit. It also has the game enviroment object where all scores are stored.

What I do is simple, I search the index of the playerscore in the game array of PlayerScores in order to later modify it.

Problem with preview is that the PlayerScore I use in the ScoreEntry_Previews is obviously not matching the UUID of the playerScore in the game array. So it won't render the preview because the var initialisation fails as it doesn't find the UUID.

How do I deal with that ?

struct ScoreEntry: View {

    @EnvironmentObject var game : Game

    @Binding var isPresented: Bool
    var playerScore: PlayerScore

    var scoreIndex: Int {
        game.playerScores.firstIndex(where: { $0.id == playerScore.id})!
    }

    var body: some View {
        HStack {
            Text("\(self.game.playerScores[self.scoreIndex].totalScore())")
        }
    }
}


struct ScoreEntry_Previews: PreviewProvider {
    static var previews: some View {
        ScoreEntry(
            isPresented: .constant(true)
            ,playerScore: PlayerScore(player: Player(name: "SomePlayer", shortName: "Steph", photoURL:"steph", color: .orange),pointsList: [1,2])
        ).environmentObject(Game())
    }
}
struct PlayerScore: Identifiable, Hashable {

    var id = UUID()
    let player: Player
    var pointsList: [Int]     

    func totalScore() -> Int {
        return pointsList.reduce(0, +)
    }

    mutating func addPoints(scoreValue: Int) {
        pointsList.append(scoreValue)
    }
}
class Game: ObservableObject {

    var players = [Player] ()
    @Published var playerScores = [PlayerScore] ()
}

Upvotes: 1

Views: 91

Answers (1)

Asperi
Asperi

Reputation: 257703

Don't auto-generate id internally, instead make it injectable, so you can create one in Preview and inject same in both places

struct PlayerScore: Identifiable, Hashable {
    var id: UUID     // << require external

Update: actually the above is still be possible approach, but below is alternate approach w/o changes in UUID.

Tested with Xcode 11.4 / iOS 13.4

Full tested module code

struct Player {
    var name: String
    var shortName: String
    var photoURL: String
    var color: Color
}

struct PlayerScore: Identifiable {

    var id = UUID()
    let player: Player
    var pointsList: [Int]

    func totalScore() -> Int {
        return pointsList.reduce(0, +)
    }

    mutating func addPoints(scoreValue: Int) {
        pointsList.append(scoreValue)
    }
}

class Game: ObservableObject {

    var players: [Player]
    @Published var playerScores: [PlayerScore]
    init(players: [Player] = [], scores: [PlayerScore] = []) {
        self.players = players
        self.playerScores = scores
    }
}

struct ScoreEntry: View {

    @EnvironmentObject var game : Game

    @Binding var isPresented: Bool
    var playerScore: PlayerScore

    var scoreIndex: Int {
        game.playerScores.firstIndex(where: { $0.id == playerScore.id})!
    }

    var body: some View {
        HStack {
            Text("\(self.game.playerScores[self.scoreIndex].totalScore())")
        }
    }
}


struct ScoreEntry_Previews: PreviewProvider {
    static var previews: some View {
        let score = PlayerScore(player: Player(name: "SomePlayer", shortName: "Steph", photoURL:"steph", color: .orange), pointsList: [1, 2])
        return ScoreEntry(isPresented: .constant(true), playerScore: score)
            .environmentObject(Game(scores: [score]))
    }
}

Upvotes: 1

Related Questions