List of relationship members fails to update when member content changes

FB13099793

I have a view which lists detail information about a SwiftData object, including a list of the members of its relationship.

The Problem:

If I use a modal presentation to edit the content of a relationship-member, upon return, the member has been updated, but the view falis to show it. The view updates correctly on adding a new member to the relationship, deleting a member, etc. But I cannot get the List to update if the content of the member changes (in a modal presentation).

A few requirements that may be of significance:

(1) The relationship (and inverse) are defined as optional because it is an eventual requirement for this app to use CloudKit synchronization.

(2) The display of the members must be ordered. For this reason, the member object contains a "ordinal" property which is used to sort the the members.

The relevant parts of the models are:

@Model
final public class Build {
	public var bld: Int

	@Relationship(inverse: \ChecklistItem.build) 
	public var checklist: [ChecklistItem]?

	public init(bld: Int) {
		self.bld = bld
		self.checklist = []
	}
}
@Model
final public class ChecklistItem {
	public var ordinal: Int
	public var title: String
	public var subtitle: String
	// etc.
	
	public var build: Build?

	public init(build: Build) {
		self.ordinal = -1
		self.title = ""
		self.subtitle = ""
		self.build = build
	}
}

The relevant parts of the view which handles the display is shown below. (Please look at the notes that follow the code for a discussion of some issues.)

struct buildDetailChecklistView: View {
	@Environment(\.modelContext) var context: modelContext
	@State private var selectedItem: ChecklistItem? = nil
	
	@Bindable var build: Build
	
	init(build: Build) {
		self.build = Build
	}
	
	var body: some View {
		VStack {
			// ... some other stuff
		}
		
		List {
			ForEach((build.checklist ?? [])
					.sorted(by: { (a,b) in a.ordinal < b.ordinal})) { item in 
				RowView(item)    // displays title, subtitle, etc.
				.swipeActions(edge: .trailing, allowsFullSwipe: false) {
					Button {
						deleteRow(item)
					} label: {
						Label("Delete", systemImage: "trash")
					}
					.tint(.red)
				}
				.swipeActions(edge: .leading, allowsFullSwipe: false) {
					Button {
						selectedItem = item
					} label: {
						Label("Edit", systemImage: "pencil.line")
					}
					.tint(.blue)
				}
			}
			.sheet(item: $selectedItem) { item in
					BuildDetailAddEditChecklistItem(item: item, 
										handler: updateChecklist(_:))
			}
		}
	}
	
	private func updateChecklist(_ item: ChecklistItem) {
		if let index = build.checklist!.firstIndex(where: { $0 == item }) {
			DispatchQueue.main.async { [index] in
				build.checklist!.remove(at: index)
				try? context.save()
				build.checklist!.insert(item, at: index)
			}
		}
	}
}

Notes:

(1) I cannot use a @Query macro in this case because of limitations in #Predicate. Every predicate I tried (to match the ChecklistItem's build member with it's parent's build object crash.)

(2) I don't want to use @Query anyway because there is no need for the extra fetch operation it implies. All of the data is already present in the relationship. There ought to be a new macro/propertyWrapper to handle this.

(3) Dealing with the required sort operation on the relationship members [in the ForEach call] is very awkward. There ought to be a better way.

(4) The BuildDetailAddEditChecklistItem() function is a modal dialog, used to edit the content of the specified ChecklistItem (for example, changing its subtitle). On return, I expected to see the List display the new contents of the selected item. IT DOES NOT.

(5) The handler argument of BuildDetailAddEditChecklist() is one of the things I tried to "get the List's attention". The handler function is called on a successful return from the model dialog. The implementation of the handler function finds the selected item in the checklist, removes it, and inserts it back into the checklist. I expected that this would force an update, but it does not.