2

I've been stuck on this for the past week and I'm asking here out of desperation. I have a project using Swift 5, and I recently enabled “Strict Concurrency Checking” to “Complete” so I can slowly migrate my project to Swift 6 without everything breaking. I have a view model that interfaces with SwiftData and has worked with no problems up until now.

I'm getting a warning for the line that, at the sortBy: [SortDescriptor(\.date)] line, says:

Type 'KeyPath<CachedMessage, Date>' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode.

Here is my code:

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
    }

}

// I already tried conforming CachedMessage to the Sendable protocol but same error.
@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
    }

}

I'm getting a warning for the line that says sortBy: [SortDescriptor(\.date)]: "Type 'KeyPath<CachedMessage, Date>' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode."

Is there a way for me to fetch data from the SwiftData ModelContext in this way in Swift 6? Here is the definition of KeyPath when I jump to definition in Xcode:

/// A key path from a specific root type to a specific resulting value type.
///
/// The most common way to make an instance of this type
/// is by using a key-path expression like `\SomeClass.someProperty`.
/// For more information,
/// see [Key-Path Expressions][keypath] in *[The Swift Programming Language][tspl]*.
///
/// [keypath]: https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID563
/// [tspl]: https://docs.swift.org/swift-book/
public class KeyPath<Root, Value> : PartialKeyPath<Root> {
}

I've also tried reading through the Apple documentation in the link provided in their definition: https://docs.swift.org/swift-book/ReferenceManual/Expressions.html#ID563 I haven't found anything else that can help me which is why I'm coming here for any potential help. ChatGPT and Claude solutions don't work (mostly just suggesting marking the function as @MainActor, or extracting the SortDescriptor(\.date) object.

Any help would be really appreciated. Thank you.

6
  • Note, you are missing a ) in ....sortBy: [SortDescriptor(\.date)] ) Commented Jan 16 at 7:31
  • 1
    KeyPath is not conform to Sendable so a quick fix is mark it as @unchecked Sendable Commented Jan 16 at 10:52
  • I can't reproduce this using Xcode 16.2 and Swift 6, maybe something changed between Swift 5 and 6? Commented Jan 16 at 11:37
  • I do see this in Xcode 16.2, Swift 5 or 6, with strict concurrency checking. I silenced this warning with “Other Swift Flags” build setting of -enable-upcoming-feature InferSendableFromCaptures as discussed in FetchDescriptor including SortDescriptor returns warning in Xcode16 or 6.0 Strict Concurrency Warning when KeyPath marked as Sendable. Commented Jan 16 at 23:45
  • 1
    Thank you @duckSern1108 for pointing me in the right direction! I needed to extend KeyPath and add @retroactive to silence this warning for now. Commented Jan 17 at 6:41

1 Answer 1

1

The warning was able to be silenced after extending KeyPath:

extension KeyPath<CachedMessage, Date>: @unchecked @retroactive Sendable {}

Will likely need to come back and change this if SwiftData changes in the future.

Sign up to request clarification or add additional context in comments.

3 Comments

As a general rule, @unchecked Sendable is not well-advised because all it does is silence the warning by making unsubstantiated claims of thread-safety. The @unchecked Sendable pattern is intended for our own custom types for which we want to inform the compiler that it is really thread-safe (because we’ve implemented our own manual synchronization mechanism). But one should avoid claiming that some other type is Sendable when you have no such assurances.
Agreed. I was trying to avoid using this if possible, but haven't been able to find another solution for SwiftData. Hence why I added @retroactive so I can come back if SwiftData changes in the future. Do you know of another way to fetch data from a SwiftData ModelContext? Or how to initialize a FetchDescriptor or KeyPath that can work with Swift 6?
I would be more inclined to leverage the InferSendableFromCaptures feature, like Holly suggested. I don’t like that, but it is better than to declare sendability for something that is not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.