1

Preview usually works fine with environment object but I'm having trouble when accessing within the object an array of objects.

The following view doesn't show in preview but works fine insimulator

import SwiftUI

struct ScoreCell: View {

    @EnvironmentObject var game : Game

    var player: Player

    var body: some View {
        VStack {  
            Text(String(self.game.playerScores[self.game.indexOfPlayerInScores(player: player)].totalScore()))
        }
    }
}

struct ScoreCell_Previews: PreviewProvider {
    static var previews: some View {
        ScoreCell(player: Player(name: "s", shortName: "Steph", photoURL: "steph", color: Color(.cyan)))
            .environmentObject(Game())
    }
}

If I replace by a hardcoded index it works just fine (like in the line below)

Text(String(self.game.playerScores[0].totalScore()))

Additional info:

  • The var "player" works ok in another line of the view
  • playerScores is an array of playerScore defined as below:
struct PlayerScore {
    let player: Player
    var pointsList: [Int]     


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

I can definitely work without preview but I have the feeling it's hiding a more generic problem that will bite me later ... Thanks for the help.

Adding code of Game to provide potential info - Thanks

import SwiftUI
import Combine

class Game: ObservableObject {

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

    func addPlayer(player: Player) {
        players.append(player)
        playerScores.append(PlayerScore(player: player, pointsList: [0]))
    }

    func addPlayer(player: Player, score: Int) {
        players.append(player)
        playerScores.append(PlayerScore(player: player, pointsList: [score]))
    }

    func indexOfPlayerInScores(player: Player) -> Int {
        return  playerScores.firstIndex(where: {$0.player.id == player.id})!
    }

    init () {
        self.addPlayer(player: Player(name: "Stephane", shortName: "Steph", photoURL:"steph", color: Color(.sRGB,red: 90/255, green: 197/255, blue: 191/255)))
        self.addPlayer(player: Player(name: "Sophie", shortName: "Sof", photoURL:"sof", color: Color(.sRGB, red: 189/255, green: 0/255, blue: 82/255)))
        self.addPlayer(player: Player(name: "Chloe", shortName: "Chloe", photoURL:"chloe", color: Color(.sRGB,red: 251/255, green: 78/255, blue: 84/255)))
        self.addPlayer(player: Player(name: "Stephane", shortName: "Gaby", photoURL:"gaby", color: Color(.sRGB,red: 255/255, green: 195/255, blue: 11/255)))
    }


}
GrandSteph
  • 1,425
  • 1
  • 13
  • 19

1 Answers1

1

The issue is definitely here

func indexOfPlayerInScores(player: Player) -> Int {
    return  playerScores.firstIndex(where: {$0.player.id == player.id})!
}

you Player does not have id in constructor, so it is autogenerated, so above method just does not find matches in run-time, because created player in preview and added by-default players in Game are different (independently of type, class or struct).

The solution: to change logic of detecting equality of Players (maybe confirming explicitly to Equatable).

Asperi
  • 123,447
  • 8
  • 131
  • 245
  • Thanks. Your detailed explanation took a while to sink in but I got it fixed. I ended up changing from player to playerScore as an argument of the struct. This moved the equality check between an element of playerScore and a playerScore within the game, and thus solving the run time error. I didn't have to conform to equable but to identifiable and hashable (for PLayerScore) – GrandSteph Mar 23 '20 at 14:13