You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中Firestore数组内对象的selectedCards字段更新问题

问题描述

我将Game对象存储在Firestore数据库中,当向selectedCards数组添加Card对象时,不想更新整个Game对象,仅需更新当前Player的selectedCards数组。

数据结构代码

struct Game: Codable {
    
    let id: String
    
    var table = Table()
    
    var players = [Player]()
    
    var player1ID = ""
    var player2ID = ""
    
    var currentPlayerSetCountShouldAnimate = false
    var opponentSetCountShouldAnimate = false
    
    init(currentPlayer: Player) {
        id = UUID().uuidString
        players.append(currentPlayer)
        players[0].playerNumber = 0
        player1ID = currentPlayer.id
    }
}

struct Player: Codable, Equatable {
    var id = UUID().uuidString
    
    var playerNumber = 0
    
    var selectedCards: [Card]
    var collectedSets: [Card]
    var selectedCardsIndexes: [Int]
    
}

更新函数代码

func updatePlayerSelectedCards(player: Player) {
    do {
        let selectedCards = try JSONEncoder().encode(player.selectedCards)
        let gameRef = Firestore.firestore().collection("Game").document(game.id)
        let playerRef = gameRef.collection("players")

        playerRef.document("\(player.playerNumber)").updateData(["selectedCards": selectedCards]) { (error) in
            if let error = error {
                print("Error updating selectedCards field: \(error)")
            } else {
                print("Successfully updated selectedCards field")
            }
        }
    } catch {
        print(error.localizedDescription)
    }
}

遇到的错误

[FirebaseFirestore][I-FST000001] WriteStream (7f9b27810c08) Stream error: 'Not found: No document to update: projects/setgamebc/databases/(default)/documents/Game/FC600D8C-85DC-4465-8648-31FFA90086EB/players/0'

我从附件图片可见players数组存在索引0,但疑惑该索引并非对应文档?是否需用FieldValue.arrayUnion?这是我的首个大型项目,希望得到帮助与解答。


解决方案

1. 核心问题分析

你的代码存在存储结构逻辑矛盾:

  • 初始化Game时,Player被存入Game结构体的players数组,意味着在Firestore中,Player是Game文档的数组字段。
  • 但更新函数里,你试图访问Game文档下的players子集合,查找对应playerNumber的文档——而你从未创建过这些子集合文档,因此会触发“找不到文档”的错误。

2. 两种可行修正方案

方案一:直接更新Game文档的嵌套数组字段(推荐)

如果Player本就是Game的一部分,无需使用子集合,直接通过点路径语法定位到对应Player的selectedCards字段进行更新即可:

func updatePlayerSelectedCards(player: Player) {
    do {
        // 使用Firestore专用编码器适配数据格式
        let selectedCardsData = try Firestore.Encoder().encode(player.selectedCards)
        let gameRef = Firestore.firestore().collection("Game").document(game.id)
        
        // 用点路径定位到players数组指定索引的selectedCards字段
        gameRef.updateData([
            "players.\(player.playerNumber).selectedCards": selectedCardsData
        ]) { error in
            if let error = error {
                print("更新失败:\(error)")
            } else {
                print("selectedCards更新成功")
            }
        }
    } catch {
        print("编码失败:\(error.localizedDescription)")
    }
}

方案二:改用子集合存储Player(适合Player数据量大的场景)

若想将Player存入Game的子集合,需在初始化Game时同步创建对应Player文档,而非仅存入Game的数组:

init(currentPlayer: Player) {
    id = UUID().uuidString
    players.append(currentPlayer)
    players[0].playerNumber = 0
    player1ID = currentPlayer.id
    
    // 同步创建子集合中的Player文档
    let gameRef = Firestore.firestore().collection("Game").document(id)
    do {
        let playerData = try Firestore.Encoder().encode(currentPlayer)
        gameRef.collection("players").document("\(currentPlayer.playerNumber)").setData(playerData)
    } catch {
        print("初始化Player文档失败:\(error)")
    }
}

完成初始化后,你的原更新函数即可正常使用(建议替换JSONEncoder为Firestore.Encoder)。

3. 关于FieldValue.arrayUnion的使用

若你的需求是往selectedCards数组追加元素,而非替换整个数组,使用FieldValue.arrayUnion会更高效,还能避免客户端与服务端数组状态不一致的问题:

func addCardToSelectedCards(player: Player, newCard: Card) {
    do {
        let cardData = try Firestore.Encoder().encode(newCard)
        let gameRef = Firestore.firestore().collection("Game").document(game.id)
        
        gameRef.updateData([
            "players.\(player.playerNumber).selectedCards": FieldValue.arrayUnion([cardData])
        ]) { error in
            if let error = error {
                print("添加卡片失败:\(error)")
            } else {
                print("卡片成功加入selectedCards")
            }
        }
    } catch {
        print("编码失败:\(error.localizedDescription)")
    }
}

内容的提问来源于stack exchange,提问作者brad8989

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:45:29