Alex Sh.
Alex Sh.

Reputation: 629

Getting String representation of Swift ReferenceWritableKeyPath is failed (returns pointer instead of type)

I have a Product class inherited from Realm Object:

@objc class Product: Object {
    @Persisted(primaryKey: true) var id: String = ""
    @Persisted var externalId: String?
    @Persisted var name: String?
}

I try to write a universal method for objects update which takes dictionary with keyPaths and values and update object with this Realm method:

func setValue(_ value: Any?, forKey key: String)

This is a computed variable for getting String representation of KeyPath:

fileprivate extension KeyPath {
    var asString: String {
        let wholeString = String(describing: self)
        let dropLeading =  "\\" + String(describing: Root.self) + "."
        let keyPathString = "\(wholeString.dropFirst(dropLeading.count))"
        return keyPathString
    }
}

And it's perfectly works during debug mode, but when I build an app with Release scheme instead of "Product.name" it returns something like "\Product.<computed 0x000000010c51ed18 (Bool)> and the app crashed trying to set value for this keypath.

If anyone has idea how to fix this, it would be really appreciated.

Update: The idea was to encapsulate working with Realm within class. This was the original method for updating Realm entities:

func updateObjects<DBEntity: Object, KeyPathType: NSObject, T>(
   ofType objectType: DBEntity.Type,
   where query: ((Query<DBEntity>) -> Query<Bool>)? = nil,
   with keyedValues: [KeyPath<KeyPathType, T>: T],
   completion: (Error?) -> Void?
)

The primary motivation behind using KeyPath instead of a simple String was to minimize errors and safeguard against crashes caused by changes in property names. Therefore, using [String : T] for the keyedValues parameter was not an option.

For now, it seems that Swift KeyPath is not guaranteed to be converted into a human-readable String, so that option is also unavailable.

I ended up modifying my update method to look like this:

func updateObjects<DBEntity: Object>(
    ofType objectType: DBEntity.Type,
    where query: ((Query<DBEntity>) -> Query<Bool>)? = nil,
    updateBlock: @escaping ([DBEntity]) -> Void,
    completion: DBOperationCompletion?
)

Now, I update object properties inside the updateBlock.

Upvotes: 1

Views: 131

Answers (0)

Related Questions