Managing Fetched Data

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Good Dogs Learn to Fetch

As you learned in the first lesson, fetching data from the data store with SwiftData and Core Data is quick and straight forward. However, the data doesn’t get returned in a straight forward way. In fact, it’s unsorted. Fortunately the @Query macro supports a sort function that, when you provide a Comparable type as a keypath (like name or date), SwiftData sorts the result by that criteria.

@Query(sort: \DogModel.name) private var dogs: [DogModel]
@Query<DogModel>(sort: \.name) private var dogs: [DogModel]
@Query(sort: [
  SortDescriptor(\DogModel.age, order: .reverse),
  SortDescriptor(\DogModel.name)
]) private var dogs: [DogModel]
@Query(filter: #Predicate<DogModel> { dog in
  dog.breed == "Labrador Retriever"
}) private var dogs: [DogModel]
DogList(sortOrder: sortOrder, filterString: filter)
  .searchable(text: $filter, prompt: Text("Filter on name or breed"))

Puppy Pictures

The app in the Simulator and device can easily support dog images because the Photos framework is integrated. What about the Canvas Previews? UIImage has a pngData property you can use. In the mock data you add UIKit support and place images in the asset catalogs. Then you can add an image to the Canvas preview with a UIImage.

image: UIImage(resource: "myPuppy").pngData()!

Best of Breed - Relationships

Relationships are links created between model types and are created as reference types in SwiftData. For example, many dogs can share the same breed name and they can visit many of the same dog parks on their daily walks. Using a breed model, you allow the dog owner to create a single breed name and then share that information between several dogs. This saves on the effort of duplication, mitigates input errors, increases query efficiency, and reduces the amount of data to store. Once created,if a breed name is edited the modification updates all the dog records that share the same breed.

One to One

There are three main types of relationships in SwiftData. The first is one-to-one, where there’s a single value on each side. A dog would have one unique dog license. An app user can have one profile, unique to that person.

// in the DogModel
var license: LicenseModel?

// in the LicenseModel
var dog: DogModel?
// in the DogModel
@Relationship(inverse: \LicenseModel.license)
var license: LicenseModel?

// in the LicenseModel
@Relationship
var dog: DogModel?
WindowGroup {
  DogListView()
    .modelContainer(for: DogModel.self)
  }
}
.modelContainer([DogModel.self, LicenseModel.self])

One to Many

The second type of relationship is a one-to-many. This would be the type of relationship where a property can be shared. You could have several dogs that are all of the same breed. In the app you have so far you have repeated the breed as a string. It would be better and less prone to input errors if you had one type of breed. Then assign that breed to the matching dogs.

Many To Many

A many-to-many relationship is the third type. In your city there are several dog parks where you can take your dog. Those parks are shared by multiple owners and their dogs. In other words, many dogs can be related to many parks.

Pick Up After Your Pet - Deleting Records

Cleaning up the data with the delete function is simple with one model. Adding other models in relationships complicates the deletion. By default, SwiftData uses Nullify as the delete rule. If you delete a dog, the breed can be left behind. However, a dog can have a license in a Permits model. You might wonder what happens when the dog record is deleted but it’s license is kept in the data store. It would become an orphaned license record. In that case, you can set the deletion rule to Cascade. When one side of a related object is deleted SwiftData will also delete the record in the other model.

// in the DogModel
@Relationship(deleteRule: .cascade)
var license: LicenseModel?
// in the DogModel
@Relationship(deleteRule: .deny)
var order: OrderModel?

Macro Polo - More on Macros

You learned a bit about @Attribute in the first lesson. There you used the externalStorage option for handling potentially large images.

@Attribute(.externalStorage) var image: Data?
@Attribute(.unique) var country: String
@Attribute(.originalName: "name") var betterName: String
@Attribute(.transformable) var strings: [[String]]
@Transient var numberOfWordsRead: Int = 0
See forum comments
Download course materials from Github
Previous: Sorting, Filtering & Relationships Next: Sort Filter Demo