In my app, an AudioFile has a one to many relationship with UpNextItem. I realised that NSBatchInsertRequest can’t handle relationships at all which is a huge drawback.
How do I handle associating each up next item to their respective audio file without high cpu usage and blocking the main thread especially since I am expecting there to be over 10k to 100k up next items?
Task {
let upNextItemsContext = DataController.shared.createNewBackgroundContext()
var objects: [[String: Any]] = []
//Delete old up next items on disk
await upNextItemsContext.perform {
let deleteRequest = NSBatchDeleteRequest(fetchRequest: UpNextItem.fetchRequest())
if let batchDeleteResult = try? upNextItemsContext.execute(deleteRequest) as? NSBatchDeleteResult, let success = batchDeleteResult.result as? Bool, success {
return
}
}
await upNextItemsContext.perform {
var index = 0
audioFiles.forEach {
defer {
index += 1
}
let properties: [String: Any] = [
"name": $0.wrappedName,
"artist": $0.wrappedArtist,
"fileExtension": $0.wrappedFileExtension,
"index": index,
"id": UUID()
]
objects.append(properties)
}
let insertRequest = NSBatchInsertRequest(entity: UpNextItem.entity(), objects: objects)
insertRequest.resultType = .objectIDs
do {
let result = try upNextItemsContext.execute(insertRequest) as? NSBatchInsertResult
let objectIDArray = result?.result as? [NSManagedObjectID]
let changes = [NSInsertedObjectsKey : objectIDArray]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes as [AnyHashable : Any], into: [moc])
} catch {
fatalError("Failed to perform batch update: (error)")
}
}
//Do a second pass over each up next item to associate with the correct audio file. But this is causing like 200% CPU usage and hangs my app for over 2-3 seconds
let upNextItems = try? moc.fetch(UpNextItem.fetchRequest())
let fetchRequest = AudioFile.fetchRequest()
upNextItems?.forEach { upNextItem in
let namePredicate = NSPredicate(format: "name = %@", upNextItem.wrappedName)
let fileExtensionPredicate = NSPredicate(format: "fileExtension = %@", upNextItem.wrappedFileExtension)
fetchRequest.predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [namePredicate, fileExtensionPredicate])
let audioFile = try? moc.fetch(fetchRequest)
upNextItem.audioFile = audioFile?.first
}
try? moc.save() //Save the changes
}