Code Monkey home page Code Monkey logo

Comments (4)

Jaycyn avatar Jaycyn commented on July 1, 2024 1

@bdkjones We've had ongoing issues with attempting to 'make it work'. Every time we went down that path, it ended up being intermittent and a bit wonky. The issues with linkingObjects becomes even bigger in a sync'ing situation as then it really fails.

Since the documentation states that functionality is not supported (it appears to work in some cases and then later it doesn't, especially with sync'ing), we decided to abandon the attempt as it may work now, "but not work on a later release", as was suggested to us by support a while back i.e. avoid doing that.

In reference to retain cycles, 100% agree! However, the objects here are not child objects - they are references to another object and are fully fledged objects on their own. Keep in mind the relationship is optional so if an object is deleted and deallocated for example, that reference goes to nil

If you run the debug memory graph on the code I provided, all of the objects play nicely - so far! :-)

We feel this functionality should exist and be supported so I strongly suggest asking for a feature request!

If you want to see what I mean, change your hard-coded observer to this

self.token = job.observe(keyPaths: ["parentProjects.parentClients.name"]) { [weak self] change in

Which should add an observer to watch for the parentClient name change. So then change the parent client name

try! realm.write {
   parentClient!.name = "parent client changed name"
}

and see what is presented to the observer - no info that the clients name was changed even though that's the property being observed via the linking objects!

Property 'parentProjects' of object Job {
	name = job name;
} changed to 'LinkingObjects<Project> <0x15af58150> (
	[0] Project {
		name = project name;
		jobs = List<Job> <0x600002971ce0> (
			[0] Job {
				name = job name;
			}
		);
	}
)'

change your observer to get the details of what changed like this

case .change(let object, let properties):
   for property in properties {
      print("Property '\(property.name)' of object \(object) changed to '\(property.newValue!)'")
}

I could be totally off-base here but that indicates it doesn't really work... Correctly. Which jibs with the docs.

from realm-swift.

bdkjones avatar bdkjones commented on July 1, 2024 1

@Jaycyn Makes sense. I hadn't seen that line in the docs previously and I haven't seen any failures with the string-based keyPaths, so I had no reason to suspect they weren't supported.

As for feature requests: that's a black hole of sadness and despair. There are requests more than 8 years old on here and every couple years someone asks for an update and...crickets. Asking Realm for features is like asking Apple for features: pointless.

from realm-swift.

Jaycyn avatar Jaycyn commented on July 1, 2024

While Realm objects are KVO compliant, linking objects are a bit different.

From the Realm Documentation on Key-Value Observation

You cannot observe LinkingObjects properties via Key-value observation.

Due to that limitation, we've implemented code to create our own relationships. While you give up some of the free stuff linking objects provides, manually handling that isn't a big deal. And it seems in your case you're not really using backlinks to multiple objects (many-many) as there is only ever one parent object.

What we do is to create the 1-many manually; here's three updated models

class Client: Object {
    @Persisted var name: String
    @Persisted var projects: RealmSwift.List<Project>
    
    func addProject(withProject: Project) {
        withProject.parentClient = self
        self.projects.append(withProject)
    }
}

class Project: Object {
    @Persisted var name: String
    @Persisted var parentClient: Client!
    @Persisted var jobs: RealmSwift.List<Job>
    
    func addJob(withJob: Job) {
        withJob.parentProject = self
        self.jobs.append(withJob)
    }
}

class Job: Object {
    @Persisted var name: String
    @Persisted var parentProject: Project!
}

and then the observer which is about the same

self.token = job.observe(keyPaths: [\Job.name, \Job.parentProject.name, \Job.parentProject?.parentClient.name]) { [weak self] change in
    switch change
    {
     case .change:
         print("Change reached!")  // Update UI here. DOES NOT FIRE for parentProject.parentClient.name changes.
     default:
         return   // We don't care about initial state.
     }
}

Then to populate

let client = Client()
let project = Project()
let job = Job()

client.name = "client name"
project.name = "project name"
job.name = "job name"

client.addProject(withProject: project)

project.addJob(withJob: job)

try! realm.write {
    realm.add(client) //adds all objects to realm
}

Then when any name is updated

let job = realm.objects(Job.self).first!
let parentProj = job.parentProject
let parentClient = parentProj?.parentClient

try! realm.write {
    job.name = "job changed name"
}

try! realm.write {
    parentProj!.name = "parent proj changed name"
}

try! realm.write {
    parentClient!.name = "parent client changed name"
}

the observer is called for each name change

Change reached!
Change reached!
Change reached!

from realm-swift.

bdkjones avatar bdkjones commented on July 1, 2024

@Jaycyn Sure, I see that approach. But there are two complications:

  1. The LinkingObjects keyPaths DO work when they're specified as Strings rather than PartialKeyPaths. This implies that Realm can handle this.

  2. Model objects where the parent has a strong reference to the child and the child has a strong reference to the parent are, in any other world, bad. It's a retain cycle. The only reason it works in Realm is that the properties are lazily-created on demand. A child object definitely shouldn't "own" its parent, though, and I'd like to avoid that anti pattern if at all possible.

from realm-swift.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.