Home > Software engineering >  SwiftUI CoreData: Insert more than 1000 entities with relationships
SwiftUI CoreData: Insert more than 1000 entities with relationships

Time:01-15

I have a SwiftUI project in which I'm using CoreData to save data fetched from an API into the device. I was trying to insert the entities in batches which worked fine until I realized that the relationships when inserting in batched are "untouched":

  • An entity Job, which has a one-to-many relationship with the entity Tag.
  • A one-to-one relationship with Category.
  • A one-to-one relationship with Type.

What I'm doing now is inserting the entities manually in a background task:

container.performBackgroundTask { context in
            for job in jobs {
                let jobToInsert = Job(context: context)
                let type = JobType(context: context)
                let category = Category(context: context)
                
                jobToInsert.id = Int32(job.id)
                ....
                do {
                    print("Inserting jobs")
                    try context.save()
                } catch {
                    // log any errors
                }
}

Is there any way to improve the performance by perhaps doing this in a way that I don't know? Because for the user, when they start the app, inserting the jobs one by one isn't a very nice experience because first, it takes a long time (more than 2 minutes) and second because my UI isn't automatically updated as I'm inserting the entities.

Thanks a lot in advance!

EDIT: I also see the memory increasing and after taking the screenshot and before I stopped the process, I saw the memory in 1.09 GB

enter image description here

EDIT 2: This is the code I used when trying to insert the jobs in batch:

private func newBatchInsertRequest(with jobs: [JobCodable]) -> NSBatchInsertRequest {
        var index = 0
        let total = jobs.count
        let batchInsert = NSBatchInsertRequest(
            entity: Job.entity()) { (managedObject: NSManagedObject) -> Bool in
                guard index < total else { return true }
                if let job = managedObject as? Job {
                    let type = JobType(context: self.container.viewContext)
                    let category = Category(context: self.container.viewContext)
                    let data: JobCodable = jobs[index]
                    job.id = Int32(data.id)
...
return batchInsert

Unfortunately, the relationship can't be built due probably to the context? since I'm getting Thread 13: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0) in the line let category = Category(context: self.container.viewContext)

CodePudding user response:

Are you calling save() on the context for every job that you insert? If so, I would start there — if you want to load a bunch of jobs all at once, you can insert all your Job instances, set up the relationships, and then only save() once at the very end (outside the loop).

This will not only save you time, but also make it appear as if all the objects show up at the same time to your UI. If that's not what you want, you can experiment with saving in batches within the loop — only call save() every thousand Job objects, for example.

  •  Tags:  
  • Related