Search code examples
iosswiftrealm

How do I set up weak references to values in a Realm data model


I am trying to make a simple iOS app that can help people split the bill at a restaurant. I have 3 main Objects, Meal, Person and Item.

I want people and items to be children of the meal. but I also want each person to have an array of items that are already children of the meal. also more than one person can share an item

how can I set up these relationships without circular references?

here is a diagram of what I mean:

Model Diagram


Solution

  • The overall structure of Realm objects is driven by the types of queries to be run against those objects. We don't know the full use case but we can infer some of those queries.

    Also, terminology wise, Realm uses List to hold array-type data but the difference is Realm List objects are persisted whereas Swift Arrays are not.

    The question mentions 'weak references' which is a memory management construct and not directly related to any Realm functionality or relationships for this use case.

    Lastly 'circular references' are mentioned which is again a memory management construct; in Realm we are going to use Relationships; both forward and inverse and one-to-one or one-to-many.

    In the diagram in the question the is no direct tie between a person and the specific food items they ordered on a meal. Yes, there is a List where the index could be used, but it's not a direct correlation and subject to change (what if the customer changes seats?)

    The solution is to add a 4th object that ties a person to the specific item(s) they ordered on a meal; CustomerFoodOrders

    class MealClass: Object {
        @Persisted(primaryKey: true) var _id: ObjectId
        @Persisted var customerFoodOrderList = RealmSwift.List<CustomerFoodOrders>()
    }
    
    class CustomerFoodOrders: Object {
        @Persisted(primaryKey: true) var _id: ObjectId
        @Persisted var customer: PersonClass!
        @Persisted var foodList = RealmSwift.List<FoodItemClass>()
        @Persisted(originProperty: "customerFoodOrderList") var linkedMeals: LinkingObjects<MealClass>
    }
    
    class PersonClass: Object {
        @Persisted(primaryKey: true) var _id: ObjectId
        @Persisted var personName = ""
    }
    
    class FoodItemClass: Object {
        @Persisted(primaryKey: true) var _id: ObjectId
        @Persisted var foodName = ""
        @Persisted(originProperty: "foodList") var linkedFoodOrders: LinkingObjects<CustomerFoodOrders>
    }
    

    with this above structure, specific items people order could be added to a meal and they are 'tied' together.

    A query could be run to return what foodItems a specific person ordered, what meals they appear on, and how many were ordered. Additionally, a total count of a persons Meals could be generated.

    The LinkingObjects in the FoodItemClass allows you to traverse the object graph from the FoodItem, to the CustomerFoodOrder (which links to the customer) and also to the specific Meal it appeared on.

    Here's a sample of getting an order (the first one), printing the order info along with the customers and what they ordered

    let order = realm.objects(MealClass.self).first!
    print("orderNum: \(order._id)")
    order.customerFoodOrderList.forEach { order in
        print(" \(order.customer.personName)")
        order.foodList.forEach { foodItem in
            print("  \(foodItem.foodName)")
        }
    }
    

    outputs to

    orderNum: 64a17f2f31399b7d2f538f9f
     Jay
      Hamburger
      Fries
     Cindy
      Steak
      Salad
    

    Reading up on Realm Relationships and in particular Inverse Relationships would allow the above models to be greatly expanded for additional functionality.

    The 'sharing an item' topic would need a some thought; many restaurants charge for the foodItem and split the price between the customers and then note it's being split for the kitchen so it can be plated separately. Accounting-wise it's only important to know the foodItem was sold, not that it was split - that becomes more of a UI thing.