Having difficulty connecting relationships during a data import

I'm trying to import some basic stop data for the trains in Chicago and there are two different custom Core Data types that I'm using to represent that data.

There are Station objects and Stop objects. The Station objects represent an entire station and the Stop objects represent a grouping of directions that exist within a Station.

The only relationships in the store are a many-one from Station to Stop (each station can have multiple groupings of directions in it, whereas each Stop can only belong to one station.)

I'm getting four main intermittent errors with my current code being related crashes (EXC_Bad Access, for example), heap corruption relating to "malloc_error_break", NSCFSet being mutated whilst being enumerated, and "CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x6000037006c0> , Objects should not be both modified and additional with userInfo of (null)" when I try to save the context.

Here is my current code that fetches the station data and performs a batch insert into Core Data.

static public func refreshData(for context: NSManagedObjectContext) async throws {

        let url = URL(string: "https://data.cityofchicago.org/resource/8pix-ypme.json")!

        let (data, _) = try await urlSession.data(from: url)

        let decoder = JSONDecoder()

        var stops = try decoder.decode([IntermediateStop].self, from: data)

        var stations = stops

        let relationships = stops

        

        let stopsRequest = NSBatchInsertRequest(entity: Stop.entity(), managedObjectHandler: { managedObject in

            guard !stops.isEmpty else {

                return true

            }

            let stop = managedObject as! Stop

            stop.update(from: stops.removeFirst())

            return false

        })

        

        let stationsRequest = NSBatchInsertRequest(entity: Station.entity(), managedObjectHandler: { managedObject in

            guard !stations.isEmpty else {

                return true

            }

            let station = managedObject as! Station

            station.update(using: stations.removeFirst())

            return false

        })

        

        guard (try context.execute(stopsRequest) as! NSBatchInsertResult).result as! Bool else {

            throw StationInitializationError.unsuccessfulInsertion

        }

        

        guard (try context.execute(stationsRequest) as! NSBatchInsertResult).result as! Bool else {

            throw StationInitializationError.unsuccessfulInsertion

        }

        

        context.refreshAllObjects()

        

        for stop in relationships {

            let stationRequest: NSFetchRequest<Station> = Station.fetchRequest()

            stationRequest.predicate = NSPredicate(format: "id = %@", argumentArray: [stop.stationid])

            let stopRequest: NSFetchRequest<Stop> = Stop.fetchRequest()

            stopRequest.predicate = NSPredicate(format: "id = %@", argumentArray: [stop.stopid])

            

            let stationResults = try context.fetch(stationRequest)

            let stopResults = try context.fetch(stopRequest)

            

            stationResults.first!.addToStops(stopResults.first!)

        }

        try context.save()

    }
Answered by thafner in 741524022

The issues were occurring due to bad multithreading: Make sure that you are performing the code that you intend on the correct queue.

static public func refreshData(for context: NSManagedObjectContext) async throws {
        let relationships = try await StationFetcher().fetch(ignoresLowDataMode: false)
        var stops = relationships
        var stations = relationships
        
        try await context.perform {
            let stopsRequest = NSBatchInsertRequest(entity: Stop.entity(), managedObjectHandler: { managedObject in
                guard !stops.isEmpty else {
                    return true
                }
                let stop = managedObject as! Stop
                stop.update(from: stops.removeFirst())
                return false
            })
            
            let stationsRequest = NSBatchInsertRequest(entity: Station.entity(), managedObjectHandler: { managedObject in
                guard !stations.isEmpty else {
                    return true
                }
                let station = managedObject as! Station
                station.update(using: stations.removeFirst())
                return false
            })
            
            guard (try context.execute(stopsRequest) as! NSBatchInsertResult).result as! Bool else {
                throw StationInitializationError.unsuccessfulInsertion
            }
            
            guard (try context.execute(stationsRequest) as! NSBatchInsertResult).result as! Bool else {
                throw StationInitializationError.unsuccessfulInsertion
            }
            
            context.refreshAllObjects()
            
            for stop in relationships {
                let stationRequest: NSFetchRequest<Station> = Station.fetchRequest()
                stationRequest.predicate = NSPredicate(format: "id = %@", argumentArray: [stop.stationid])
                let stopRequest: NSFetchRequest<Stop> = Stop.fetchRequest()
                stopRequest.predicate = NSPredicate(format: "id = %@", argumentArray: [stop.stopid])
                
                let stationResults = try context.fetch(stationRequest)
                let stopResults = try context.fetch(stopRequest)
                
                stationResults.first!.addToStops(stopResults.first!)
            }
            
            if context.hasChanges {
                try context.save()
            }
        }
    }
Accepted Answer

The issues were occurring due to bad multithreading: Make sure that you are performing the code that you intend on the correct queue.

static public func refreshData(for context: NSManagedObjectContext) async throws {
        let relationships = try await StationFetcher().fetch(ignoresLowDataMode: false)
        var stops = relationships
        var stations = relationships
        
        try await context.perform {
            let stopsRequest = NSBatchInsertRequest(entity: Stop.entity(), managedObjectHandler: { managedObject in
                guard !stops.isEmpty else {
                    return true
                }
                let stop = managedObject as! Stop
                stop.update(from: stops.removeFirst())
                return false
            })
            
            let stationsRequest = NSBatchInsertRequest(entity: Station.entity(), managedObjectHandler: { managedObject in
                guard !stations.isEmpty else {
                    return true
                }
                let station = managedObject as! Station
                station.update(using: stations.removeFirst())
                return false
            })
            
            guard (try context.execute(stopsRequest) as! NSBatchInsertResult).result as! Bool else {
                throw StationInitializationError.unsuccessfulInsertion
            }
            
            guard (try context.execute(stationsRequest) as! NSBatchInsertResult).result as! Bool else {
                throw StationInitializationError.unsuccessfulInsertion
            }
            
            context.refreshAllObjects()
            
            for stop in relationships {
                let stationRequest: NSFetchRequest<Station> = Station.fetchRequest()
                stationRequest.predicate = NSPredicate(format: "id = %@", argumentArray: [stop.stationid])
                let stopRequest: NSFetchRequest<Stop> = Stop.fetchRequest()
                stopRequest.predicate = NSPredicate(format: "id = %@", argumentArray: [stop.stopid])
                
                let stationResults = try context.fetch(stationRequest)
                let stopResults = try context.fetch(stopRequest)
                
                stationResults.first!.addToStops(stopResults.first!)
            }
            
            if context.hasChanges {
                try context.save()
            }
        }
    }
Having difficulty connecting relationships during a data import
 
 
Q