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

TestFlight构建中SwiftData嵌套@Model排序崩溃问题

SwiftData嵌套模型排序在TestFlight构建中崩溃的解决方案

问题重现

SwiftData模型代码

@Model
class ClosedDealModelContainer {
    var closedDeal: ClosedDealModel

    init(closedDeal: ClosedDealModel) {
        self.closedDeal = closedDeal
    }
}

@Model
public class ClosedDealModel: Hashable {

    public enum DealType: Codable {
        case long
        case short
    }

    public let id: String
    public let userId: String
    public var buy: BuyModel
    public var sell: SellModel
    public var type: DealType
    public var symbol: String
    public var currency: String

    public init(
        id: String,
        buy: BuyModel,
        sell: SellModel,
        type: DealType,
        symbol: String,
        userId: String,
        currency: String
    ) {
        self.id = id
        self.buy = buy
        self.sell = sell
        self.type = type
        self.symbol = symbol
        self.userId = userId
        self.currency = currency
    }
}

@Model
public class BuyModel {
    public let fee: Double
    public let date: Date
    public let price: Double
    public let count: Double

    public init(fee: Double, date: Date, price: Double, count: Double) {
        self.fee = fee
        self.date = date
        self.price = price
        self.count = count
    }
}

@Model
public class SellModel: Equatable {
    public let fee: Double
    public let date: Date
    public let price: Double
    public let count: Double

    public init(fee: Double, date: Date, price: Double, count: Double) {
        self.fee = fee
        self.date = date
        self.price = price
        self.count = count
    }
}

查询代码

let descriptor = FetchDescriptor<ClosedDealModelContainer>(
    predicate: #Predicate { container in
        container.closedDeal.userId == userId
    },
    sortBy: [SortDescriptor(\.closedDeal.sell.date)]
)
let items = try modelContext?.fetch(descriptor)

崩溃情况

  • Debug/本地Release版本运行正常
  • TestFlight构建中在static PersistentModel.keyPathToString(keypath:)行崩溃
  • 改为按ClosedDealModel.id排序可正常运行,问题锁定在嵌套对象的键路径解析

问题原因

TestFlight默认使用Fastest, Smallest [-Os]全优化编译,该优化会剥离SwiftData解析嵌套键路径所需的元数据,导致keyPathToString无法正确识别跨多个@Model实体的嵌套路径。Debug模式下优化关闭,元数据完整,因此不会崩溃。


解决方案

方案1:添加顶层计算属性(推荐)

在ClosedDealModelContainer中新增直接映射嵌套属性的计算属性,通过顶层属性排序绕过嵌套键路径解析:

@Model
class ClosedDealModelContainer {
    var closedDeal: ClosedDealModel
    
    // 新增计算属性,直接返回嵌套的销售日期
    var sellDate: Date {
        closedDeal.sell.date
    }

    init(closedDeal: ClosedDealModel) {
        self.closedDeal = closedDeal
    }
}

修改查询代码,改用顶层计算属性排序:

let descriptor = FetchDescriptor<ClosedDealModelContainer>(
    predicate: #Predicate { container in
        container.closedDeal.userId == userId
    },
    sortBy: [SortDescriptor(\.sellDate)]
)

方案2:调整编译优化级别(临时方案)

在Target的Build Settings中,将TestFlight对应的Build Configuration的Optimization Level改为Fastest [-O],或关闭Whole Module Optimization。该方法会降低部分性能优化,仅作为临时过渡方案使用。

方案3:手动指定字符串键路径(不推荐)

绕过键路径自动解析,手动传入字符串形式的路径。注意该方法依赖SwiftData内部路径格式,版本更新可能失效:

let descriptor = FetchDescriptor<ClosedDealModelContainer>(
    predicate: #Predicate { container in
        container.closedDeal.userId == userId
    },
    sortBy: [SortDescriptor(\ClosedDealModelContainer.closedDeal.sell.date, keyPathString: "closedDeal.sell.date")]
)

内容的提问来源于stack exchange,提问作者Igor Ševtšenko

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:58:34