UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

SOLVED: Best practices blending SwiftUI and Core Data?

Forums > SwiftUI

Hello!

I am new to Swift and all things related to development in the iOS/Apple space, so I hope this is a good place to ask. Recently I completed a handful tutorials which focus on iterating through data coming from core data and representing it in SwiftUI

Paul Hudson did this in one of his Sunday videos where he made an app to create and iterate over dad jokes. The key take aways for me were:

  • Understanding managed object context and how fetch requests play together is important (and Paul went over this in his video)
  • @FetchRequest is a SwiftUI type that handles much of the fetching and sorting of data from Core Data for me, and I soon learned that this is specific to SwiftUI. I can't use this in say, a class within my app (more on this below)

But now I want to take it one step further, fetch data from an API, and store it within Core Data. What I've done so far:

  • Created a separate class that will handle calling the external API and paginating through it
  • Coupled this class into my SwiftUI view and call it from within when I want to load more data (e.g. go to next page)

This is where I want to understand if I am approaching it the right way. My application is fetching data and storing in CD but it's been difficult to find best practices on this so maybe I can get some help here?

First, I initialize my class in the view like so:

struct ContentView: View {
    ...
    var groupList = GroupList()

    // I also use FetchRequest to show the data in this view, but for brevity, I'll only show a limited amount of information:
    @Environment(\.managedObjectContext) var moc

    @FetchRequest(entity: Group.entity(), sortDescriptors: [
        NSSortDescriptor(keyPath: \Group.name, ascending: true)
    ]) var groups: FetchedResults<Group>

    var body: some View {
        VStack {
            List(groups) { group in
                .... // show group view
                .onAppear {
                    // This paginates through the API
                    self.groupList.loadMoreGroups(group)
                }
            }
        }
    }  
}

With respect to Core Data, the class has the following notables:

class GroupList {    
    fileprivate  var appDelegate: AppDelegate = {
        UIApplication.shared.delegate as! AppDelegate
    }()

    let fetchRequest: NSFetchRequest<Group> = Group.fetchRequest()

And when I want to do something with Core Data, I do the following everywhere I need anything (a single result or more):

subscript(position: Int) -> Group {
        let moc = appDelegate.persistentContainer.viewContext
        let groupItems = try! moc.fetch(fetchRequest)
        return groupItems[position]
 }

I repeat this pattern (let moc and moc.fetch) in a few functions. This immediately raises questions.

When I load data from the API to store into Core Data, it's similar:

let moc = appDelegate.persistentContainer.viewContext

for groupMap in groupMapList {
        guard let title = groupMap["title"] as? String else {
            continue
        }

        let newGroup = Group(context: moc)
        newGroup.name = title
}

do {
    try moc.save()
} catch {
     print("Whoops! \(error.localizedDescription)")
}

This saves the data into Core Data but again, I tread with caution!

So finally, my questions in a nut shell:

  • What are the appropriate patterns & best practices with using Core Data in this way, alongside SwiftUI views?
  • Given what you see, anything that would bite me hard here?
  • Should I even be using Core Data to fetch data from an API and store it? A lot of online chatter about this and it's generally a mix of "Use Realm or something simpler" and "Learn CD, understand its best practices, and you'll do well". I lean to the latter, but my brain is not wired to iOS dev yet so maybe fetching data from an external API and caching it in my app (to improve future reloads and state for the user) is better served by something that isn't Core Data

Hopefully that's a reasonable amount of things to ask in a single topic. Thank you in advance! :)

3      

Hi, I think Core Data is perfectly valid for this and it has gotten way better in recent years. It used to be a case that Realm was considerably faster to set up and had prettier API but I dont think that applies anymore. The ain thing is that CD is Apple framework so you dont need to handle dependencies and worry about support in future iOS versions.

Regarding your code. Fetch requests are expensive and you are fetching all groups to get single one. That is not really ideal. I would either load all groups into memory when app starts assuming you dont have 100s of them or you can cache individual ones and fetch just the one you need if it not in cache. Managed object context also lets you iterate over registered objects (mesning those fetched or freshly inserted) so you can try that before executing the fetch.

4      

Also I personally dont like having CD code in app delegate but it is pretty common since that is what Xcode generates out of the box. I usually have like a Database class which is a singleton and handles the CD init plus stuff like background context and its proper merging of changes into the main context.

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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

@nemecek-filip,

Thanks! This is helpful and appreciate you offering a variety of insight

when app starts assuming you dont have 100s of them or you can cache individual ones and fetch just the one you need if it not in cache

This is likely what I will need to do, given I expect to have hundreds of entires on a single page, but only a subset of them will likely be stored in Core Data (whatever is current state). What is the standard caching approach within iOS?

Also I personally dont like having CD code in app delegate but it is pretty common since that is what Xcode generates out of the box.

Interesting! I don't understand this yet wrt AppDelegate and just do it because I find writing online which supports it. Do you have more information on your/alternative approaches?

Thank you

3      

hi,

@nemecek-filip's comment about not overburdening the AppDelegate is important, especially in XCode 12/iOS 14 with its new App structure (where there is no AppDelegate).

i have not worked with XCode 12 beta3 much yet, but i do have an XCode 11.6/iOS 13.6 project out there that places the CD code in its own PersistentStore singleton, which is created and injected into the SwiftUI environment in the SceneDelegate. not all of it is my own idea, but rather an amalgamation of what's out there now on this forum and the Apple Developer's Forum, with references in the comments to relevant on-line discussions.

you can find my ShoppingList project on Github (with apologies to some readers, and @nemecek-filip as well, who have probably seen me post this reference before).

hope that helps,

DMG

4      

@bartek if you are interested I could post my approach to CD to my blog with explanation.

The CD stack is nothing special that it needs to go to AppDelegate. I think it is rather unfortunate that Apple chooses to put it there..

3      

Filip,

I don't want to hijack the OPs thread, but hoping you'll be able to put together that blog post.

Just found your blog https://nemecek.be/ and wish I saw it after banging my head in the wall on advanced string comparsions.

I just started with the Xcode12 beta and followed the singleton approach you discuss here. It was recommended towards the end of this thread https://developer.apple.com/forums/thread/650876

But as I'm experimenting, I'm finding myself asking more questions. Through this website, if I understood it correctly, the logic with CD in SwiftUI is to run a fetch in every view ("we should carve off the functionality we want into a separate view, then inject values into it". https://www.hackingwithswift.com/books/ios-swiftui/dynamically-filtering-fetchrequest-with-swiftui Is this still true with SwiftUI 2.0 and the new wrappers? If we implement a Database class, will that be less expensive than a FetchRequest? What functionality do you put in that class vice the view? How does this all roll back into the original question of applying data from an API?

4      

@danieleprice1 I am really glad and happy that my post helped you out. I plan to publish my Core Data approach soon :thumbs_up:

3      

@delawaremathguy Thank you! Love me a sample project and appreciate you publishing as you learn. I will definitely dig deeper into this.

@nemecek-filip Lots of good insight here and in your conversation with @danieleprice1 :) Yes, I think we'd all appreciate a post on CD and I've already bookmarked your blog for further reading. I don't think I am at the place to understand why this is necessary but it's good to have learning to work towards, so thank you!

4      

So here is the promised post :-) https://nemecek.be/blog/41/my-approach-to-setting-up-core-data-stack I have to say that I was quite nervous when publishing this because Core Data is such a big and complex topic but on the other hand this solution has been working for me for some time..

Feel free to ask further questions

5      

@nemecek-filip

Amazing! I read it and seems fairly clear. I do believe I still have a lot to learn to understand why you'd use this but I think people new to CD will appreciate it as a starting point

For the record, some questions I have after reading your piece are:

  1. Good point highlighting about background processing but how exactly is this used in a proper application? Would be great to expand this example!
  2. As someone new to iOS Development, I am all in SwiftUI and I am beginning to think how this pattern (and Core Data in general) tie in with ObservableObject .. which is most definitely a shift in thinking for existing iOS devs, it seems :)

No need to respond and carry on this thread, just figured you'd appreciate some feedback. Amazing for you to write that out!

3      

Hacking with Swift is sponsored by RevenueCat

SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure your entire paywall view without any code changes or app updates.

Learn more here

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.