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

如何对比较值为自定义类型的SortDescriptor进行编解码?

如何对比较值为自定义类型的SortDescriptor进行编解码?

我最近也碰到过一模一样的问题,咱们先还原一下场景:假设我们定义了一个带标题和日期的自定义Item类型:

struct Item {
    let title: String
    let date: Date
}

官方文档明确说SortDescriptor<Item>默认就符合Codable协议,所以上面的代码编译完全没问题。于是我们尝试编码一个针对Item.title的本地化标准排序描述器:

do {
    let descriptor = SortDescriptor<Item>(\.title, comparator: .localizedStandard)
    let encoded = try JSONEncoder().encode(descriptor)
    print(String(data: encoded, encoding: .utf8)!)
} catch {
    debugPrint(error)
}

结果尴尬的事情来了——编译一路绿灯,但运行时直接编码失败,抛出的错误信息如下:

Swift.EncodingError.invalidValue(Foundation.SortDescriptor<__lldb_expr_46.Item>.AllowedComparison.comparableString((extension in Foundation):Swift.String.StandardComparator(options: __C.NSStringCompareOptions(rawValue: 833), isLocalized: true, order: Foundation.SortOrder.reverse), \Item.title), Swift.EncodingError.Context(codingPath: [], debugDescription: "Encoding SortDescriptor with values of type non-NSObject Compared is unsupported.", underlyingError: nil))

问题根源

其实报错信息已经把原因说得明明白白:针对非NSObject类型的SortDescriptor编码是不被原生支持的。虽然SortDescriptor本身标注了Codable,但这只是“部分支持”——当你用的是NSString这类NSObject子类的属性时没问题,但咱们的Item.title是Swift原生的String,即便它能和NSString桥接,编码时底层也没处理这种桥接逻辑,直接导致运行时崩溃。

靠谱的解决方案

既然原生的路走不通,我们可以换个思路:把排序的核心信息(键路径、排序顺序、比较器类型)抽出来做成一个完全可控的Codable自定义结构体,再通过这个结构体生成对应的SortDescriptor。

举个实际的例子:

// 定义可编码的排序键、顺序、比较器枚举
enum SortKey: String, Codable {
    case title, date
}

enum SortOrder: String, Codable {
    case ascending, descending
}

enum SortComparator: String, Codable {
    case localizedStandard, standard
}

// 封装排序信息的可编码结构体
struct CodableSortInfo: Codable {
    let key: SortKey
    let order: SortOrder
    let comparator: SortComparator
}

// 扩展生成SortDescriptor
extension CodableSortInfo {
    func makeSortDescriptor() -> SortDescriptor<Item> {
        switch key {
        case .title:
            let comparator: String.StandardComparator = comparator == .localizedStandard ? .localizedStandard : .standard
            let sortOrder: SortOrder = order == .ascending ? .forward : .reverse
            return SortDescriptor(\.title, comparator: comparator, order: sortOrder)
        case .date:
            let sortOrder: SortOrder = order == .ascending ? .forward : .reverse
            return SortDescriptor(\.date, order: sortOrder)
        }
    }
}

现在我们就可以安全地编解码排序信息,再转换成需要的SortDescriptor:

do {
    // 编码排序信息
    let sortInfo = CodableSortInfo(key: .title, order: .ascending, comparator: .localizedStandard)
    let encodedData = try JSONEncoder().encode(sortInfo)
    print(String(data: encodedData, encoding: .utf8)!)
    // 输出:{"key":"title","order":"ascending","comparator":"localizedStandard"}
    
    // 解码并生成SortDescriptor
    let decodedInfo = try JSONDecoder().decode(CodableSortInfo.self, from: encodedData)
    let descriptor = decodedInfo.makeSortDescriptor()
    // 现在这个descriptor就可以正常使用啦
} catch {
    debugPrint(error)
}

这种方式虽然需要额外写一些代码,但胜在稳定可控,完全避开了原生SortDescriptor编码的坑。

备注:内容来源于stack exchange,提问作者parapote

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 07:04:40