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

使用外键时Supabase Swift解码失败问题排查

Supabase Swift SDK 关联外键查询解码失败:期望Dictionary<String, Any>却得到字符串

使用Supabase Swift SDK查询关联外键表时,调用.select("*, account(*)")会触发解码错误,提示“Expected to decode Dictionary<String, Any> but found a string instead”。但如果逐个指定所有字段(而非使用*通配符),查询就能正常解码。

复现信息

模型定义

struct Transaction: Codable {
    var id: UUID
    var name: String
    var subtitle: String?
    var merchant: String?
    var amount: Decimal
    var tax_deductible: Bool = false
    var date: Date
    var account: TransactionAccount?
}

enum AccountType: String, Codable, CaseIterable {
    case debit
    case savings
    case loan
    case credit
    case cash
}

struct TransactionAccount: Codable, Hashable {
    var id: UUID
    var name: String
    var description: String?
    var type: AccountType
    var color: String
}

SDK查询代码

do {
    return try await Database.shared
        .client
        .database
        .from("transactions")
        .select("*, account(*)")
        .in("type", value: scope.transactionTypes.map({ $0.rawValue }))
        .execute()
        .value
} catch {
    return []
}

解码错误详情

Printing description of error:
▿ DecodingError
  ▿ typeMismatch : 2 elements
    - .0 : Swift.Dictionary<Swift.String, Any>
    ▿ .1 : Context
      ▿ codingPath : 2 elements
        ▿ 0 : _JSONKey(stringValue: "Index 2", intValue: 2)
          ▿ rep : Rep
            - index : 2
        - 1 : CodingKeys(stringValue: "account", intValue: nil)
      - debugDescription : "Expected to decode Dictionary<String, Any> but found a string instead."
      - underlyingError : nil

问题原因

使用*通配符会返回transactions表的所有字段,其中包含原始的外键字段(account,类型为字符串UUID),而.select("account(*)")又尝试将该外键扩展为关联的TransactionAccount对象。此时返回的JSON中,account字段可能因Supabase处理逻辑冲突,部分条目返回原始外键值而非关联对象,导致Codable解码器无法匹配预期的TransactionAccount类型。

解决方案

  1. 明确指定查询字段(推荐)
    放弃使用*通配符,逐个列出transactions表的所有字段及关联对象的字段,确保返回的account仅为关联对象:

    .select("id, name, subtitle, merchant, amount, tax_deductible, date, account(*)")
    

    此方法已验证可行,能彻底避免原始外键字段与扩展对象的冲突。

  2. 排除原始外键字段
    若需保留*通配符,可通过Supabase的字段排除语法去掉原始外键字段(假设外键列名为account):

    .select("*, !account, account(*)")
    

    该语法会先返回所有字段,再排除原始的account外键字段,最后返回扩展后的account对象。

  3. 检查数据与权限

    • 确认数据库中所有transactions表的account外键均存在对应的transaction_accounts表记录,避免因关联缺失导致返回原始外键值。
    • 检查Supabase的RLS(行级安全)策略,确保当前用户有权限访问transaction_accounts表的所有字段,权限不足可能导致关联对象无法正常返回, fallback到原始外键值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 20:43:22