3

I have a standard SwiftUI list setup, powered by Core Data FetchRequest.

struct SomeView: View {

    var container: Container
    var myObjects: FetchRequest<MyObject>

    init(container: Container) {
        let predicate : NSPredicate = NSPredicate(format: "container = %@", container)
        self.container = container
        self.myObjects = FetchRequest<MyObject>(entity: MyObject.entity(), sortDescriptors: [NSSortDescriptor(key: "date", ascending: true)], predicate: predicate)
    }

    var body: some View {
        VStack(spacing: 0.0) {
            List(myObjects.wrappedValue, id: \.uniqueIdentifier) { myObject in
                rowView(for: myObject, from: self.myObjects.wrappedValue)
            }
        }
    }
}

Everything works well when items are added and deleted. RowView returns a view that presents different content based on various properties of myObject.

Problem: when I modify a particular myObject elsewhere in the app (change one of its properties), and save the associated Core Data ManagedObjectContext, the List row representing that item is not updated/refreshed in the UI.

Possibly a cause for this is that I am updating my Core Data object by setting a property, that in turn sets another property. Maybe the associated signaling doesn’t reach the right place, and I should emit more notifications here.

Code in MyObject. ObjectType is an enum, typeValue is int32 backing this, that actually gets stored in CD database.

var type: ObjectType {
    get {
        return ObjectType(rawValue: typeValue)!
    }
    set {
        self.typeValue = newValue.rawValue
    }
}

How do I cause a list row to update when the backing Core Data object is modified and saved elsewhere in the app?

8
  • what is "elsewhere"? AFAIK, if you modify something elsewhere, that should mean, the view presenting the data is not active, and when it becomes active again, it should pull the new data, shouldnt it? Commented Jan 16, 2020 at 15:01
  • Basically you are correct. In this case, the modification happens from a context menu button. I have a context menu button on the item, where I modify the item and save the managed object context. Yes, if I make the modification, then navigate away and come back, the item is correctly updated. Commented Jan 17, 2020 at 9:41
  • I thought about it more, and... is there any particular reason you are not using the @FetchRequest and @Environment property wrappers? I think those ensure immediate reload. Without them you only reload when the View is recalculated again. If you are not familiar with their usage, I can post an example. Commented Jan 17, 2020 at 10:23
  • Yes there is a reason I am doing FetchRequest this way. Note that there is a predicate on "container", which is passed to this view as a parameter. As far as I know, there isn’t a way to do this kind of predicate with property wrappers. Commented Jan 17, 2020 at 10:29
  • Interesting point though. Elsewhere in my app, I do have a propertywrapper-driven FetchRequest which actually reloads info in-place as expected. So this might possibly be the root cause, I need to test this. Commented Jan 17, 2020 at 10:30

1 Answer 1

8

I finally figured this out on my own. The fix was not in the list, but further down the stack, in RowView.

RowView code was such:

struct RowView: View {
    var myObject: MyObject
    // Other code to render body etc
}

When doing this, the RowView works as expected, but it treats myObject as immutable. Any changes to myObject don’t cause a view redraw.

The one-keyword fix is to add @ObservedObject to the declaration:

struct RowView: View {
    @ObservedObject var myObject: MyObject
}

It now works as expected, and any updates to MyObject cause a redraw.

Sign up to request clarification or add additional context in comments.

3 Comments

Hi Jaanus, I'm struggling with something similar, I have a root view with a list, then a RowView like yours that receives the object, I added the @ObservedObject in there but update is not reflected still. The RowView takes the user to a detailview, from there the user is taken to another view where changes to the model are performed. Once done the user is taken back to detail view but no changes reflected. If I go back to the list and then back to the detail, the changes are there. Sounds familiar ? Any clues ? Thanks!
Thank you. You saved me hours of time. This is so simple but I overlooked it. Thought it was SwiftUI bug 😂
Hi, @Jaanus, Can you look into my similar question. stackoverflow.com/questions/65303742/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.