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.