Search code examples
swiftswiftuiswiftdataswift-data-relationship

Configure relationships with SwiftData in Preview


We are developing an iOS app using SwiftData. We have Bucket and BucketItem models where a Bucket can have many BucketItem, so there is a relationship between Bucket and BucketItem. Below is a simplified version of the models.

import Foundation
import SwiftData

@Model
class Bucket {
  var name: String
  var shared: Bool
  
  @Relationship(deleteRule: .cascade, inverse: \BucketItem.bucket) var items = [BucketItem]()
  
  init(name: String, shared: Bool) {
    self.name = name
    self.shared = shared
  }
}

import Foundation
import SwiftData

@Model
class BucketItem {
  var content: String
  var bucket: Bucket
  
  init(content: String, bucket: Bucket) {
    self.content = content
    self.bucket = bucket
  }
}

We show a list of Bucket with a count of how many BucketItem it has. This is working in the app on the device and simulator, however the relationship is not working on the Preview. Below is a simplified version of this component with the Preview configuration

import SwiftUI
import SwiftData

struct BucketsData: View {
 var bucket: Bucket //this is provided by a parent component that uses @Query to fetch the Buckets
  
  var body: some View {
    List {
      Section(bucket.name) {
        Text("Items: \(bucket.items.count)")
      }
      
    }
  }
}

#Preview {
  let config = ModelConfiguration(isStoredInMemoryOnly: true)
  let container = try! ModelContainer(for: Bucket.self, configurations: config)
  
  let testBucket = Bucket(name: "Test Bucket", shared: true)
  container.mainContext.insert(testBucket)
  
  let testItem = BucketItem(content: "Test Item", bucket: testBucket)
  container.mainContext.insert(testItem)
  
  return BucketsData(bucket: testBucket)
    .modelContainer(container)
}

The list show the "Test Bucket" with a count of 0 items. It appears that for some reason the items array is not getting populated with the related BucketItem. How can we fix this?


Solution

  • You can fix this by making BucketItem.bucket optional, like so:

    @Model
    class BucketItem {
      var content: String
      var bucket: Bucket?
      
        init(content: String, bucket: Bucket? = .none) {
        self.content = content
        self.bucket = bucket
      }
    }
    

    Why does this work? I don't know. SwiftData is weird.

    Joakim Danielson already said it in the comments, but I'm posting this as an answer so people will know that this question is answered. I've tested this solution and it works.