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

Xcode14.3+Firebase9.6.0映射Firestore文档时遇无参数调用错误

Firestore文档映射自定义Codable结构体时编译错误问题(Xcode14.3 + Firebase9.6.0)

使用Xcode14.3、Firebase9.6.0开发时,尝试将Firestore文档映射到实现了Identifiable、Codable协议的自定义Game结构体,遵循Firebase官方编码指南后仍遇到以下问题:

  • 第一种写法虽提示「Arguments passed to call takes no arguments」错误,但可正常编译运行;
  • 第二种优化写法(包含细分错误处理逻辑)却无法通过编译。

Game结构体定义

struct Game: Identifiable, Codable {  
    @DocumentID var id: String?             // Firestore文档UUID
    var nr: Int?
    var status: String?
    var league: String
    var homeTeam: String
    var homeTeamLogoUrl: String?
    var guestTeam: String
    var guestTeamLogoUrl: String?
    var runs: Runs?
    var scheduledDate: Date
    var startTime: Date?
    var endTime: Date?
    var ballpark: String
    var city: String
    var scorerID: String?
    var score: Count?
    var bases: BaseStatus?
    var inning: Int?
    var inningHalf: String?
    var inningResults: [InningResult]?
}

两种写法对比

第一种:报错但可运行的代码

func getLiveGames(country: String, league: String, season: Int) async {
    // 声明临时直播赛事列表
    var liveGames = [Game]()

    let leagueRef = db.collection("liveGames")
    
    let snapshot = try? await leagueRef.getDocuments()

    if snapshot != nil {
        for docs in snapshot!.documents {
            do {
                let result = try docs.data(as: Game.self)
                print(result)
            } catch {
                print(error)
            }  
        }
    } else {
        self.errorMessage = "No documents in 'liveGames' collection"
        print(self.errorMessage)
    }
}

第二种:无法编译的代码

func getLiveGames(country: String, league: String, season: Int) async {
    // 声明临时直播赛事列表
    var liveGames = [Game]()

    let leagueRef = db.collection("liveGames")
    
    let queryLiveGames = try? await leagueRef.getDocuments()
     
    if queryLiveGames != nil {
        liveGames = (queryLiveGames!.documents.compactMap({ (queryDocumentSnapshot) -> Game? in
        let result = Result { try? queryDocumentSnapshot.data(as: Game.self) }

        switch result {
        case .success(let game):
             if var game = game {
                 // 从DocumentSnapshot成功初始化Game实例
                 self?.errorMessage = nil
                 liveGames.append(game)
                 return game
             }
             else {
                 // DocumentSnapshot为nil或初始化出nil值
                 self.errorMessage = "Game document doesn't exist."
                 return nil
             }
         
        case .failure(let error):
        // 无法从DocumentSnapshot初始化Game实例
             switch error {
                 case DecodingError.typeMismatch(_, let context):
                 self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
                 case DecodingError.valueNotFound(_, let context):
                 self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
                 case DecodingError.keyNotFound(_, let context):
                 self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
                 case DecodingError.dataCorrupted(let key):
                 self.errorMessage = "\(error.localizedDescription): \(key)"
                 default:
                 self.errorMessage = "Error decoding document: \(error.localizedDescription)"
             }
             return nil
        }
        }))
    } else {
        self.errorMessage = "No documents in 'liveGames' collection"
        print(self.errorMessage)
    }
}

问题原因与修正方案

问题分析

  1. 第一种写法的报错:属于Xcode编辑器的语法检测误报,Firebase的data(as:)方法在Swift并发环境下偶尔会触发编辑器签名识别bug,实际编译时能正确解析方法,因此可以正常运行。
  2. 第二种写法的编译失败:
    • 逻辑冲突:在compactMap中同时执行liveGames.append(game)和将compactMap返回值赋值给liveGames,导致重复操作且编译器类型推断混乱;
    • Result类型错误:try? queryDocumentSnapshot.data(as: Game.self)返回Game?,导致Result类型为Result<Game?, Error>,后续switch分支的类型处理出现歧义;
    • self可选性错误:若当前函数属于非可选类型的实例,self不需要加?,这会直接导致编译报错。

修正后的可编译代码

func getLiveGames(country: String, league: String, season: Int) async {
    var liveGames = [Game]()
    let leagueRef = db.collection("liveGames")
    
    // 先判断快照是否存在,避免强制解包
    guard let queryLiveGames = try? await leagueRef.getDocuments() else {
        self.errorMessage = "No documents in 'liveGames' collection"
        print(self.errorMessage)
        return
    }
    
    // 用compactMap直接构建结果数组,移除内部append操作
    liveGames = queryLiveGames.documents.compactMap { document -> Game? in
        do {
            let game = try document.data(as: Game.self)
            self.errorMessage = nil
            return game
        } catch {
            // 细分解码错误处理
            switch error {
            case DecodingError.typeMismatch(_, let context):
                self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
            case DecodingError.valueNotFound(_, let context):
                self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
            case DecodingError.keyNotFound(_, let context):
                self.errorMessage = "\(error.localizedDescription): \(context.debugDescription)"
            case DecodingError.dataCorrupted(let key):
                self.errorMessage = "\(error.localizedDescription): \(key)"
            default:
                self.errorMessage = "Error decoding document: \(error.localizedDescription)"
            }
            return nil
        }
    }
    
    // 此处可添加liveGames的后续业务逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 04:10:00