Swift 6迁移时SwiftData Fetch更新:KeyPath不兼容Sendable问题
我被这个问题困扰一周了,无奈求助。我的项目基于Swift 5,最近把“Strict Concurrency Checking”设置为“Complete”,想逐步迁移到Swift 6同时避免全面崩溃。原本正常工作的SwiftData交互ViewModel出现了警告:在sortBy: [SortDescriptor(\.date)]这一行提示:
Type 'KeyPath<CachedMessage, Date>' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode.
相关代码如下:
import SwiftUI import SwiftData @MainActor final class MyViewModel: ObservableObject { private(set) var modelContext: ModelContext? private var tempMessagesCache: [Message] = [] @Published var messages: [Message] = [] func setModelContext(_ modelContext: ModelContext) { self.modelContext = modelContext self.modelContext!.autosaveEnabled = false } func fetchCachedMessages(for chatGroupId: String) { do { let descriptor = FetchDescriptor<CachedMessage>( predicate: #Predicate { $0.chatGroupId == chatGroupId }, sortBy: [SortDescriptor(\.date)] ) if let messages = try modelContext?.fetch(descriptor) { tempMessagesCache = messages.map({ Message(cachedMessage: $0) }) } } catch { // Handle error } } } struct Message { let id: String let chatGroupId: String let date: Date // more metadata here // other initializers here init(cachedMessage: CachedMessage) { self.id = cachedMessage.id self.chatGroupId = cachedMessage.chatGroupId self.date = cachedMessage.date // more metadata here } } // 我已经尝试让CachedMessage遵循Sendable协议,但警告依然存在。 @Model final class CachedMessage { @Attribute(.unique) private(set) var id: String private(set) var chatGroupId: String private(set) var date: Date // more metadata here // other initializers here init(message: Message) { self.id = message.id self.chatGroupId = message.chatGroupId self.date = message.date // more metadata here } }
我已经尝试让CachedMessage遵循Sendable协议,但警告依然存在。请问在Swift 6中,如何通过SwiftData ModelContext以该方式获取数据?
1. 显式确认KeyPath的Sendable一致性
问题根源在于Strict Concurrency模式下,编译器无法自动推断KeyPath<CachedMessage, Date>符合Sendable。可以通过显式标注KeyPath的类型来解决:
func fetchCachedMessages(for chatGroupId: String) { guard let modelContext = modelContext else { return } do { // 显式标注KeyPath为Sendable类型 let dateKeyPath = \.date as Sendable KeyPath<CachedMessage, Date> let descriptor = FetchDescriptor<CachedMessage>( predicate: #Predicate { $0.chatGroupId == chatGroupId }, sortBy: [SortDescriptor(dateKeyPath)] ) let cachedMessages = try modelContext.fetch(descriptor) tempMessagesCache = cachedMessages.map(Message.init) } catch { print("获取缓存消息失败:\(error)") } }
2. 确保CachedMessage完全符合Sendable
虽然@Model修饰的类默认应该符合Sendable,但显式声明可以帮助编译器确认一致性,同时检查所有属性是否都是Sendable类型(String、Date均为原生Sendable类型,无需额外处理):
@Model final class CachedMessage { // 现有代码... } // 显式添加Sendable协议扩展 extension CachedMessage: Sendable {}
3. 优化ViewModel的线程安全
由于ViewModel标记了@MainActor,所有ModelContext操作已限制在主线程执行,符合Sendable要求。可以进一步优化代码避免强制解包,提升安全性:
func setModelContext(_ modelContext: ModelContext) { self.modelContext = modelContext modelContext.autosaveEnabled = false }
内容的提问来源于stack exchange,提问作者Ryan Kanno

