My use case is based on the following model:
struct Person {
let name: String
let houses: [House]
}
struct House {
let owner: Person
}
Now, ideally I would like to maintain a bidirectional relationship that requires every house to have exactly one owner where an owner should also know all its houses.
Using the above data structures, is it possible to create instances of House
and Person
such that there is a relationship between the two and the objects are essentially pointing at each other?
I guess the phrasing of this already is somewhat misleading, because due to the value semantics of struct
s, they don't really point anywhere but are only holding copies of values. It seems to be like it should be obvious that it's not possible to create these two objects with a bidirectional relationship, but I still wanted to be sure and ask this questions here!
An obvious solution would also be to make houses
and owner
variables using var
instead of let
when declaring them, then the relationship could be maintained in the initializer of each struct
:
struct Person {
let name: String
var houses: [House]
init(name: String, houses: [House]) {
self.name = name
self.houses = houses
self.houses = houses.map { (house: House) in
var newHouse = house
newHouse.owner = self
return newHouse
}
}
}
struct House {
var owner: Person
init(owner: Person) {
self.owner = owner
var newHouses = self.owner.houses
newHouses.append(self)
self.owner = Person(name: owner.name, houses: newHouses)
}
}
However, what if I want to keep houses
and owner
constant? As I said, it seems to be obvious that it's not possible, but I'm wondering if there's some fancy (maybe functional) way to achieve this? I was thinking about lenses, which can be used as getters and setters when dealing with immutable models.
What you're describing sounds more like an ORM than a language feature, and also doesn't sound appropriate to handle with value types like structs. How do you expect the language to know that owner
is the inverse relationship property of houses
and needs to be maintained accordingly? This is something that needs to be enforced with code is not possible with the Swift language alone.
It sounds like you're trying to maintain some kind of model object graph, which is a problem that's been solved many times over. You should take a look at Realm DB and Core Data both of which offer managed bidirectional relationships. You'll find however that neither of these are implemented with structs. The problem with using value types is that they are copy-on-write. As soon as you mutate one struct in the object graph, you'd need to reassign all of it's related things to the new, mutated copy, which then in turn are mutated and would need to update all of their related structs. Reference semantics (classes) make maintaining an object graph much easier as mutations don't produce new instances.
What you want is not possible using structs. Your current solution (with var
) doesn't work like you think it does: all values are independent copies. Consider the following example:
let p = Person(name: "Nick", houses: [])
let h = House(owner: p)
h.owner.houses
p.houses
Because each copy is independent, this means p
isn't the same as h.owner
.
If you need a bidirectional relationship, you should use objects with weak
pointers (to break the reference cycle). Alternatively, you could think to see if you need the bidirectional relationship.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With