SwiftData + Transferable

Suppose we have a SwiftData Model:

@Model
class Contact {
    var firstName: String?
    var lastName: String? 
}

We make this model Codable (I’ll skip showing the implementation; it’s standard). Now we want to allow our user to drag and drop instances of this model object around. Maybe from a List row to a group in a sidebar, or out of our app and into another app like Mail or Notes.

We declare a custom UTType and want to export as JSON. So we attempt to do this:

extension Contact: Transferable
{
    static var transferRepresentation: some TransferRepresentation {
        CodableRepresentation(for: Contact.self, contentType: .ourCustomUTType)
    }
}

In Swift 6 language mode, the compiler throws a fit:

Conformance of 'Contact' to 'Sendable' is unavailable: PersistentModels are not Sendable, consider utilizing a ModelActor or use Contact's persistentModelID instead

It seems strange that Transferable implies Sendable, because we’re not sending the actual thing; we’re sending a representation of it. A JSON blob, some Data, a String, etc. (I guess maybe the encoding happens on any thread in the pool.)

What’s the elegant way of dealing with this in Swift 6? I don’t want to transfer just the persistentModelID. It’s pointless to transfer that when the user drags to Mail, Notes, etc. I want to construct a JSON file and transfer that.

Thus far, all I can think to do is create a “wrapper” struct:

struct ContactShim: Transferable
{
    let jsonData: Data
    
    init(_ contact: Contact) {
        // convert contact to JSON data.
    }
    
    static var transferRepresentation: some TransferRepresentation {
        // provide jsonData instead of Codable.
    }
}

Surely there’s a better way?

1 Like