Lets say i have an realm object stored in my db (indeed is much more complex):
class Dog: Object {
@objc dynamic var name = ""
@objc dynamic var age = 0
}
Now lets say i want to modify this object cause you can edit the Dog object. To verify you can actually save this object you have to call a server, and if its ok, you can save it.
The problem is that for modify the object i only have 2 options: One is importing Realm and modifying the object from the db (the option that i dont want, cause i need the object in my views and i dont want to import Realm there), and the option that im aiming for is to create a copy of the realm Object to a regular struct.
struct DogStruct {
var name = ""
var age = ""
}
So i can easily change those values and then if servers is ok, i can save it. There is any trick or way to do this transform? I have to manually do the transform between Dog and DogStruct and its very tedious, since Dog have nested vars and so on.
Don't know if this what you need but you can use default constructor for realm object.
let copyDog = Dog(value: dog)
@mkowalski87 but you can't change those properties of copyDog without a realm write transactions, isnt?
@ivangodfather Actually you can, because such created object is detached from its realm.
@mkowalski87 seems for nested objects doesn't work :-?
after let copyDog = Dog(value: dog)
dog.name = "This works"
dog.owner.name = "This breaks"
I tried also doing copyDog.owner = Person(value: copyDog.owner) just in case before dog.owner.name but the same crash. Isnt weird?
@ivangodfather In case of relations I guess you have to implement copy method and do it manually in similar way.
@ivangodfather we are doing something similar, in our case we wanted a detached object from Realm so we are using this (we do this when we read an object from Realm so we can use that instance from any thread):
import Realm
import RealmSwift
protocol DetachableObject: AnyObject {
func detached() -> Self
}
extension Object: DetachableObject {
func detached() -> Self {
let detached = type(of: self).init()
for property in objectSchema.properties {
guard let value = value(forKey: property.name) else {
continue
}
if let detachable = value as? DetachableObject {
detached.setValue(detachable.detached(), forKey: property.name)
} else { // Then it is a primitive
detached.setValue(value, forKey: property.name)
}
}
return detached
}
}
extension List: DetachableObject {
func detached() -> List<Element> {
let result = List<Element>()
forEach {
if let detachableObject = $0 as? DetachableObject,
let element = detachableObject.detached() as? Element {
result.append(element)
} else { // Then it is a primitive
result.append($0)
}
}
return result
}
}
Then on your object (if it inherits from Object) you can just do object.detached() 馃憤
Hope it helps!
@robertofrontado makes a lot of sense! thx u!
As of now, Dec 2020, there is not proper solution of this issue. We have many workarounds though.
Here one I have been using, and one with less limitations in my opinion.
class Dog: Object, Codable{
@objc dynamic var breed:String = "JustAnyDog"
}
class RealmHelper {
//Used to expose generic
static func DetachedCopy<T:Codable>(of object:T) -> T?{
do{
let json = try JSONEncoder().encode(object)
return try JSONDecoder().decode(T.self, from: json)
}
catch let error{
print(error)
return nil
}
}
}
//Suppose your Realm managed object: let dog:Dog = RealmDBService.shared.getFirstDog()
guard let detachedDog = RealmHelper.DetachedCopy(of: dog) else{
print("Could not detach Note")
return
}
//Change/mutate object properties as you want
detachedDog.breed = "rottweiler"
As you can see we are piggy backing on Swift's JSONEncoder and JSONDecoder, using power of Codable, making true deep copy no matter how many nested objects are there under our realm object. Just make sure all your Realm Model Classes conform to Codable.
Though its NOT an ideal solution, but its one of the most effective workaround.
Most helpful comment
@ivangodfather we are doing something similar, in our case we wanted a detached object from Realm so we are using this (we do this when we read an object from Realm so we can use that instance from any thread):
Then on your object (if it inherits from
Object) you can just doobject.detached()馃憤Hope it helps!