iosswiftuiswiftdataswift-data-relationship

Lazy loading of dependent relationship in SwiftData


I have an Order model that contains Items

@Model
class Order: Decodable {
  @Attribute(.unique) var orderId: String
  var items: [Item]
 }

When inside the list view, I fetch all the orders, the Items are not loaded and shows empty

var body: some View {
    List {
        DynamicQuery(orderDescriptor) { orders in
            ForEach(orders) { order in
        ForEach(order.items) { item in
          if let sandwichName = item.data?.sandwichName {
            Text(sandwichName)
          }
        }
      }
    }
    .onReceive(toolbarModel.$selectedDate) { newDate in
        print("Date changed to: \(newDate)")
        getOrders(ofDate: newDate) { result in
            switch result {
                case .success:
                    print("Order Fetch successful for Date \(newDate)")
                case .failure(let error):
                    print("Order Fetch failed with \(error)")
            }
        }
    }
}

This is how I am inserting the orders into modelcontext

let orders = try JSONDecoder().decode([Order].self, from: jsonData)
                                    

  for order in orders {
//  print(order.items) // crashes the app
    modelContext.insert(order)
  }

I tried defining the relationshipKeyPathsForPrefetching but doesnt work.

private var orderDescriptor: FetchDescriptor<Order> {
        var fetchDescriptor = FetchDescriptor(
            predicate: Order.currentOrders(with: toolbarModel.selectedDate),
            sortBy: [SortDescriptor(\Order.time)]
        )
//      fetchDescriptor.relationshipKeyPathsForPrefetching = [\.items]

        return fetchDescriptor
    }

I know the relationship are lazy load but I assume if I am referring the items inside the list, it should load? If I reload the app and wait a bit, I could see some items so seems like its loading but not right away. Anything that I am missing?


Solution

  • Turns out, the inverse relationship was causing the issue. Previously, I had the relationship between Order and Item defined as

    @Model
    class Order: Decodable {
       @Attribute(.unique) var orderId: String
       var items: [Item]
    }
    @Model
    class Item: Decodable {
       @Attribute(.unique) var orderItemId: String
       @Relationship(inverse: \Order.items) var order: Order?
    }
    

    Changed that to

    @Model
    class Order: Decodable {
        @Attribute(.unique) var orderId: String
        @Relationship(inverse: \Item.order) var items = [Item]()
    }
    @Model
    class Item: Decodable {
        @Attribute(.unique) var orderItemId: String
        var order: Order?
    }
    

    and it worked.