Adding to a one-to-many relationship not updating UI

Viewed 123

I have a one-to-many relationship in core data of plan -> recipe. plan.recipes is of type NSSet?, so I have created custom NSManagedObject classes with computed properties to convert these into arrays, adding an extra property recipesArray:

public var recipesArray: [Recipe] {
    let set = recipes as? Set<Recipe> ?? []
    
    return set.sorted {
        $0.wrappedName < $1.wrappedName
    }
}

I then display this list in a View using a ForEach, using the recipesArray property. A subview of this view calls plan.addToRecipes(recipe: Recipe), to add a new object to the relationship. I then save.

The issue is, the ForEach in the parent view does not react to this addition. If I refresh the view by navigating away, then the new recipe is shown, but the View is not automatically updated when the new recipe is added.

Does anyone know how to do this? Should I be using the original recipes property instead of this custom array one?

1 Answers

You need to make another FetchRequest for Recipes using a predicate that equals a given plan, e.g. something like this:

struct RecipesView: View {
    var fetchRequest: FetchRequest<Recipe>
    var recipes: FetchedResults<Recipe> { fetchRequest.wrappedValue }

    init(plan: Plan) {
        let sortDescriptors = ...
        let predicate = NSPredicate(format: "plan = %@", plan)
        fetchRequest = FetchRequest(sortDescriptors: sortDescriptors, predicate: predicate, animation: .default)
    }

    var body: some View {
        ForEach(recipes) { recipe in
            RecipeView(recipe: recipe) // it's important to not access recipe's properties (firing a fault) until inside a sub-view's body.
        }
    }
}

Note: There is currently a bug in FetchRequest that results in body always invoked even when this View is init with the same plan and thus same FetchRequest. This is because the FetchRequest struct inits a new object inside it every time, causing it and consequently this View to appear as changed to SwiftUI. I reported this bug so hopefully they fix it. You could workaround it in the meantime with a wrapper View that takes the plan as a let so body won't be called.

Related