Best practice to deal with "clone" objects? (sync on tap/click)

In many situations it will be a bad UX to sync/save every single change the user makes. Because of this I open a temp(In memory) realm and then I copy that object from the temp realm to the synced realm. I am unsure if this however Is considered good practice, not to mention the mess it creates, having to keep a temp object for each object (user, tempUser, playlist, tempPlaylist, etc.).

I would really appreciate if you can point me to the right direction.

Does the temporary object need to be stored in Realm at all, or could it just be an in-memory object that you work on and then save to Realm when you’re done (that’s how I’d normally do it for new objects, a bit trickier when changing an existing object)?

Creating a temporary object is how we do it -

let myUnmanagedObject = MyObject(value: managedObjectToCopy)

creates an unmanaged, editable object. Then, once done, you can save it in Realm.

The downside is that any child objects, like objects in List, also need to have unmanaged copies made and then there are a couple of issues if it has embedded objects.

But overall, yes, that’s a common practice.

That being said, we don’t sustain them in memory very long - they stay around while the user is actively editing it, like on a sheet or detail view but when that’s completed, they are saved and disposed of.

OK, what you describe here is how I’d do it. I got confused by your original description about storing them in a temporary realm. As unmanaged Objects, I don’t believe that they’re stored in any realm.

The OP was creating an in-memory realm and copying the objects to it, which would also be unmanaged objects.

However, it doesn’t seem like that’s really needed for this use case if just the individual objects are being worked with.

Creating in-memory copies (as I indicated above) should suffice for most situations including when a user is making changes to a specific object. I would not suggest leveraging in-memory realms unless there’s a really a need it.

Thanks for your replies! If I don’t create an in-memory (temp realm), I get an error for trying to write outside a write transaction when I try to change a value.

Here is an example

func addToTempRealm(newValue: String) {
    try! state.tempRealm.write {
        let tempUser = state.tempRealm.create(type(of: state.user!), value: state.tempUser, update: .modified)
        tempUser.card!.label = newValue
        state.tempUser = tempUser
    }
}

Maybe I am doing something wrong.

I think it will be an amazing feature to have a local and synced state per object. So if I try to edit an object outside a write transaction, Realm will save it locally but if I change a value within a write transaction, it will sync the changes.

You shouldn’t get an error if you create and modify an Object that hasn’t yet been added to Realm.

I am curious why you’re creating an in-memory realm to start with. If you’re just editing an object, creating a copy of it will suffice - there’s not need to also add it to a Realm until you’re ready to persist it.

Also, I am a little suspicious of this

create(type(of: state.user!)

If you want to create an unmanaged copy of a Realm object, you would use that object class per my above post. Does your .user class contain any relationships? If so, that’s not supported with .create.

You are right, having a in-memory realm is just an overkill. However I am still confused about what’s the best practice to avoid realtime sync (when let’s say a user is editing their profile and they are not sure if they want to save the changes).

Thanks for your attention

Let me provide a high level scenario:

Suppose your app has a listing of items, like an address book.

The contact list is a Results object populated from Realm that acts as a datasource for a tableView.

self.contactResults = realm.objects(ContactClass.self) //self.contacts is the tableView dataSource
self.contactTableView.reloadData()

Then suppose when a contact is double clicked (macOS) or selected and Edit button tapped (iOS) another view is shown that allows that contacts details to be edited. For that process you want to pass an unmanaged Realm object to the sheet or details view to populate it and be editable.

let detailView = DetailView() //create the detail view
let managedContact = self.contactResults[selectedRow] //get the selected contact
let unmanagedContact = ContactClass(value: managedContact) //create an unmanaged, editable copy
detailView.populateWith(contact: unmanagedContact)

From there the user can edit the contact info on the detailView, updating the unmanaged contact object along the way and when complete, write it to Realm.

This gives the app the ability to not sync or attempt to update a managed object outside a write transaction - and the user can click ‘Cancel’ and no data will be affected

Now, you may say to yourself

“self, why not just pass the managed object and update it within a write transaction”

The answer is that in some cases, you want to maintain the data in an actual object instead of creating arrays and other vars to hold the data while editing. For example suppose the contact had a bunch of embedded Addresses - keeping those in a tidy list within a the ContactClass object makes them easier to work with; adding, editing and removing addresses can be done right within the object because it’s unmanaged.

EDIT: Sadly this doesn’t work if the object has nested objects(list, embedded). I would have to go back to the in-memory solution. Or maybe deconstruct the object and use local @State to deal with the changes before syncing.

@Jay Thanks for the explanation, it worked! I actually tried this before but it wasn’t working (changes were persisted) because I had my unmanaged object in an ObservableObject.

This is what I have now when the user goes to EditProfileView,

.onAppear {
                let unmanagedUser = User(value: state.user!)  
                tempUser = unmanagedUser
            }

It actually does work but you need to also make a copy, known as a deep copy, of the embedded objects. I had a question about that topic last year

and there’s a standing issue as well

What we do it when we create an unmanaged copy of an object we also use another function within that object that iterates over the embedded object list and creates unmanaged copies of each (that was suggested in the link I included above)

Thanks @Jay! It’s a shame that this isn’t included out of the box, sounds like an essential feature as not every user action requires instant sync. I will try to experiment with this deep copy route.

Thanks again for your time

After scouting the web for solution I found these:

Converting the realm object to JSON. However I can’t conform to Codable (on latest RealmSwift).

Another way it to .detach() the object but it’s not working with EmbeddedObject

So I guess there is no “best practice” because Realm ignores this use case altogether.

Ok! Managed to find a solution for using unmanaged objects with nested lists and embedded objects.

Just make sure that every object and embedded object has Codable or you will get errors.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.