Dynamic Core Data with SwiftUI Tutorial for iOS

Learn how to take advantage of all the new Core Data features introduced in iOS 15 to make your SwiftUI apps even more powerful. By Mark Struzinski.

4.5 (12) · 5 Reviews

Download materials
Save for later
Share
You are currently viewing page 3 of 3 of this article. Click here to view the first page.

Updating Sorts

Trying to sort right now won’t work at all. Try changing the sort options — eventually, the app will crash and display this message:

Thread 1: "UITableView internal inconsistency: encountered out of bounds global row index while preparing batch updates"

This error is caused by a mismatch in section and row data when you sort on one property but group on another. The fetch results must be sorted in section order for the list to work. To fix this, you need to add an additional property to your sort option struct. Open FriendSort.swift and add a new property to FriendSort:

let section: KeyPath<Friend, String>

Next, update sorts to add the new section parameter to all of the initializers:

static let sorts: [FriendSort] = [
  FriendSort(
    id: 0,
    name: "Meeting Place | Ascending",
    descriptors: [
      SortDescriptor(\Friend.meetingPlace, order: .forward),
      SortDescriptor(\Friend.name, order: .forward)
    ],
    section: \Friend.meetingPlace),
  FriendSort(
    id: 1,
    name: "Meeting Place | Descending",
    descriptors: [
      SortDescriptor(\Friend.meetingPlace, order: .reverse),
      SortDescriptor(\Friend.name, order: .forward)
    ],
    section: \Friend.meetingPlace),
  FriendSort(
    id: 2,
    name: "Meeting Date | Ascending",
    descriptors: [
      SortDescriptor(\Friend.meetingDate, order: .forward),
      SortDescriptor(\Friend.name, order: .forward)
    ],
    section: \Friend.meetingDay),
  FriendSort(
    id: 3,
    name: "Meeting Date | Descending",
    descriptors: [
      SortDescriptor(\Friend.meetingDate, order: .reverse),
      SortDescriptor(\Friend.name, order: .forward)
    ],
    section: \Friend.meetingDayDescending)
]

Each sort option now has a section key path. Note that the two meeting date options have different key paths — without this, you get a crash when switching between the two date options.

Go back to ContentView.swift. Here, you can replace the initial section with the new property from the default sort option. In the @SectionedFetchRequest property, replace the section identifier with the following code:

sectionIdentifier: FriendSort.default.section,

Still in ContentView.swift, update the application of sorts to the list by replacing the body of the .onChange closure with this::

let request = friends
request.sectionIdentifier = selectedSort.section
request.sortDescriptors = selectedSort.descriptors

This code might seem a little strange — why is let request = friends there? Why not just update friends directly? The reason is that you are now making two changes to the fetch request, but they both need to be evaluated at the same time. Each time you reference friends, it’ll commit any changes to the fetch request. So if you change friends.sectionIdentifier, then change friends.sortDescriptors, the fetch request with the updated section identifier will be evaluated, before you have had chance to update the sort descriptors. As you’ve seen earlier, this can cause crashes, as the results have to be in section order. Pulling out the request into a local reference and updating that means that the changes won’t be evaluated until friends is accessed again when the view body is recomputed.

Build and run to test your new changes. You’ll see sorting by meeting place will group by section and perform a sort on meeting place name.

Sorted and sectioned by place

Performing a sort by meeting date will change the grouping to meeting date, then perform a sort on that property.

Sorted and sectioned by date

Where to Go From Here?

Download the completed project files by clicking the Download Materials button at the top or bottom of the tutorial.

If you want to dig deeper into working with Core Data and SwiftUI together, check out this Core Data with SwiftUI tutorial and this Core Data with CloudKit tutorial.

We hope you enjoyed this tutorial. If you have any questions or comments, please join the forum discussion below!