BLACK FRIDAY SALE: Save 50% on all my Swift books and bundles! >>

SOLVED: Core Data, @FetchRequest, and MVVM

Forums > SwiftUI

I've spent the last few months working on a macOS project that incorporates Core Data. I will release the app to the appstore soon but the primary motivation for writing it was to learn SwiftUI.

In that time I've seen a lot of different ways to utilise Core Data and I wasn't really happy with any of them from a design perspective. My first approach was to use the @FetchRequest property wrapper in my contentView but that quickly turned into putting absolutely everything into contentView. The app worked properly and I could have (and probably should have) just released it then but I really wasn't happy with it from a design perspective. At this point I decided to refactor the program to MVVM.

However Core Data and MVVM comes with quite a few questions that need answering themselves. I thought about using the DataController example model that is demonstrated on the Ultimate Portfolio App tutorial but I wasn't quite 100% happy with this. Using this approach is clearly good because it abstracts away Core Data from your view and it also makes it easier to change your data stores down the road, but I wanted somthing that made full use of the idiomatic features of Core Data which includes @FetchRequest, but I wasn't quite sure how to do it.

Additionally MVVM seems to mean different things to different people. It seems that many people take the Model component to mean exclusively the data or the database, and they suggest putting the business logic into the View Model. For me the Model represents a model for the application, of which the data is only a portion. This means I've got a heavy Model and skinny Views and ViewModels.

In the end I was able to come up with a solution that worked and also fit my desire to integrate Core Data idiomatically. My view code ended up incorporating one @ObervedObject which was my ViewModel, and two @FetchRequests. User actions filter down to the Model which changes it's state which then filters back to the View via either the @ObservedObject or the @FetchRequests.

I'm not sure if I'd use the same approach on future projects (unless I were sure they not require cross platform) but I'm glad I did it for this one.

Anyway my post doesn't really have a point other than to share my experience and to hear other people's on how they've dealt with this.



i feel your pain!

i think the Core Data connection with SwiftUI Views is a little bit of a kludge, frankly -- i think @FetchRequest is just a fancy wrapper for an NSFetchedResultsController. and i have written/seen many Views that either wind up having a var body: some View property with hard-to-read code that can run 100+ lines, or have a whole bunch of functions in them that make them look like the massive view controllers (MVC) of UIKit.

the situation is further complicated by the fact that Core Data provides (class) objects and SwiftUI really wants to work with structs. you have access to the object itself -- you never tell a VM to do something to it (an "intent"), and you hope that the @FetchRequest will back you up on this. you might look at the 2021 Standford University course with Paul Hegarty on this notion of view models and "intent."

my journey continues to this day. i once tried a simple CD project named ShoppingList in SwiftUI where i went out of my way to use more MVVM and avoid @FetchRequest completely. i learned to like it, especially since deleting CD objects is nothing but a pain in SwiftUI if any View considers it an @ObservedObject.

then i updated this project to ShoppingList14 and removed all the ViewModel code and went back to @FetchRequest and i still had to invent workarounds for certain deletions of CD objects.

i consulted within the last year on two apps now on the app store on the CD implementation, and for one of those, we used a hybrid management of CD and in-memory objects through a view model. in a current project that is "almost ready for the app store," i have more of a @FetchRequest reliance, although i still have to invent a VM or two to watch for changes in CD that use an NSFetchedResultsController.

my current belief: if you only create and modify CD objects, you can probably stick with @FetchRequest or any combination of it together with the occasional VM -- although i really do hate adding lots of boiler-plate code to so many SwiftUI Views. i try to be sure that the Views i write do not import CoreData and move many functions onto the Core Data classes themselves via an extension, making each CD class somewhat of a mini-VM by itself.

but if you allow for deletion of CD objects and some views maintain @ObservedObject references to them, then you may need to circumvent @FetchRequest -- and at that point a VM that uses an NSFetchedResultsController might look more attractive. you might take a look at Donny Wals book on practical Core Data on this point.

so my definitive answer to your unasked question: i think the jury's still out on how best to use SwiftUI and CD. i was hoping for some news at WWDC2021. maybe next year ...

hope that helps,


Edit after the fact: you might also consider reading this article by Dave DeLong, which seems to be working on the edges of what i think we're both talking about. i was intrigued; although i have not pursued it myself.


Thanks for your thoughtful reply DMG, and also for your links.

I've found that I've had to do a few things that sort of feel like hacks when combining the use of a view model with @FetchRequest, and you're right the problems did relate to deletion.

For example I populate a Picker with a @FetchRequest and I use the instance of the entity for the selection rather than using the index of the selection. This seems to work fine until I need to delete the selected entity (which I perform in my model rather than in the view) because if I set the selection to nil it will break the Picker.

I also use this "create and delete" approach when initialising the selector in case the @FetchRequest is empty.


Hacking with Swift is sponsored by Guardsquare

SPONSORED AppSweep by Guardsquare helps developers automate the mobile app security testing process with fast, free scans. By using AppSweep’s actionable recommendations, developers can improve the security posture of their apps in accordance with security standards like OWASP.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.