@Model and Encodable cause the error Ambiguous use of 'setValue(for:to:)'

I test with a simple class and it can compile:

@Model
final class Book: Encodable {
    var name: String

    enum CodingKeys: CodingKey {
        case name
    }

    public func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(name, forKey: .name)
    }

}

Then I try with the Trip class from sample and it cause an error:

@Model final class Trip: Encodable {
    :
    enum CodingKeys: CodingKey {
        case name, destination, endDate, startDate, bucketList
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)

        try container.encode(name, forKey: .name)
        try container.encode(destination, forKey: .destination)
        try container.encode(endDate, forKey: .endDate)
        try container.encode(startDate, forKey: .startDate)
        try container.encode(bucketList as! Set<BucketListItem>, forKey: .bucketList)
    }

And I got the error Ambiguous use of 'setValue(for:to:)'

I tried make BucketListItem and LivingAccommodation Encodable and still have error.

Post not yet marked as solved Up vote post of bill2022 Down vote post of bill2022
500 views

Replies

I'm not coming in with a solution, but just to say that I'm seeing the exact same error. SwiftData unfortunately seems to be very poorly rolled out so far, even for a beta.

Editing to add: it seems as if I'm only getting those errors on attributes marked with the @Relationship marker.

I've now found a (hopefully temporary) solution.

I added the following methods to each of my @Model classes:

func getValue<T>(for key: KeyPath<YourClassName, T>) -> T {
        return self[keyPath: key]
    }
    
func setValue<T>(for key: ReferenceWritableKeyPath<YourClassName, T>, to newValue: T) {
        self[keyPath: key] = newValue
    }

and those errors went away. But, of course, a litany of new ones arose, so hopefully yours goes better.

While I finally got it working with this, the litany of ensuing errors resolved, each one of these setters and getters is causing runtime crashes.

This is not the first time I am getting such issue. And it's very annoying. But here is my solution. First, I add the following public disambiguation functions (most are useless, provided for completion, we're there, why not make them):

public extension BackingData {
    func setPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value>, to newValue: Value) where Value : PersistentModel {
        setValue(forKey: key, to: newValue)
    }
    func setPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value?>, to newValue: Value?) where Value : PersistentModel {
        setValue(forKey: key, to: newValue)
    }
    func setRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self.Model, Value>, to newValue: Value) where Value : RelationshipCollection, OtherModel == Value.PersistentElement {
        setValue(forKey: key, to: newValue)
    }

    func getPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value>) -> Value where Value : PersistentModel {
        getValue(forKey: key)
    }
    func getPersistentModelValue<Value>(forKey key: KeyPath<Self.Model, Value?>) -> Value? where Value : PersistentModel {
        getValue(forKey: key)
    }
    func getRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self.Model, Value>) -> Value where Value : RelationshipCollection, OtherModel == Value.PersistentElement {
        getValue(forKey: key)
    }
}

public extension PersistentModel {
    func setPersistentModelValue<Value>(forKey key: KeyPath<Self, Value>, to newValue: Value) where Value : PersistentModel {
        setValue(forKey: key, to: newValue)
    }
    func setPersistentModelValue<Value>(forKey key: KeyPath<Self, Value?>, to newValue: Value?) where Value : PersistentModel {
        setValue(forKey: key, to: newValue)
    }
    func setRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self, Value>, to newValue: Value) where Value : RelationshipCollection, OtherModel == Value.PersistentElement {
        setValue(forKey: key, to: newValue)
    }

    func getPersistentModelValue<Value>(forKey key: KeyPath<Self, Value>) -> Value where Value : PersistentModel {
        getValue(forKey: key)
    }
    func getPersistentModelValue<Value>(forKey key: KeyPath<Self, Value?>) -> Value? where Value : PersistentModel {
        getValue(forKey: key)
    }
    func getRelationshipCollectionValue<Value, OtherModel>(forKey key: KeyPath<Self, Value>) -> Value where Value : RelationshipCollection, OtherModel == Value.PersistentElement {
        getValue(forKey: key)
    }
}

Then, for every type, I add up a generic getter/setter (only the setter is used in the initial value, but hey, I'm there!) for my precise type I want. To use the initial Book example:

public extension BackingData {
    func setValue(forKey key: KeyPath<Self.Model, Book>, to newValue: Book) {
        setPersistentModelValue(forKey: key, to: newValue)
    }
    func setValue(forKey key: KeyPath<Self.Model, Book?>, to newValue: Book?) {
        setPersistentModelValue(forKey: key, to: newValue)
    }
    func getValue(forKey key: KeyPath<Self.Model, Book>) -> Book {
        getPersistentModelValue(forKey: key)
    }
    func getValue(forKey key: KeyPath<Self.Model, Book?>) -> Book? {
        getPersistentModelValue(forKey: key)
    }
}

Finally, for every type that has relationships, I add up an extension to the type itself. In initial example, let's assume Trip includes some Book, as Book? or [Book]:

extension Trip {
    func setValue(forKey key: KeyPath<Trip, Book>, to newValue: Book) {
        setPersistentModelValue(forKey: key, to: newValue)
    }
    func setValue(forKey key: KeyPath<Trip, Book?>, to newValue: Book?) {
        setPersistentModelValue(forKey: key, to: newValue)
    }
    func getValue(forKey key: KeyPath<Trip, Book>) -> Book {
        getPersistentModelValue(forKey: key)
    }
    func getValue(forKey key: KeyPath<Trip, Book?>) -> Book? {
        getPersistentModelValue(forKey: key)
    }
}

This relies on the fact the swift compiler first tries non-generic versions of a function. In which case, we created some. Then, it tries to use the generic version, which is ambiguous. So we are providing the exact version it wants first. This pattern can be used for other ambiguous situations, such as the RelationshipCollection, OtherModel setters and getters. I have provided them in the generic functions too.