I create new Song NSManagedObjects in a background managed object context since I expect the user to select as many as 10k to 100k songs.
AudioFile is a core data entity in my app that models any files that are of type audio. Song is the “middleman”/”intermediate” entity that has a “many to one” relationship to a playlist.
When the user presses “Done” in the navigation bar of my SwiftUI view, it gets passed onto my view model array to reflect the changes in a list view.
At the moment, I am not sure how to properly pass the NSManagedObjects from the background context to the main queue’s managed object context without causing performance issues.
class PlaylistSongsSelectionVM: ObservableObject {
private var songsToBeAdded = [Song]()
private let moc = DataController.shared.container.viewContext
private let backgroundContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
private init() {
backgroundContext.parent = moc
}
func addAudioFileToPlaylist(audioFile: AudioFile) {
backgroundContext.perform { [weak self] in
guard let self else { return }
//Creates dummy songs for testing
for _ in 1...10000 {
let newSong = Song(moc: backgroundContext, audioFile: audioFile)
songsToBeAdded.append(newSong)
}
}
}
///Call this method when user presses done to add the songs to the respective playlist
func addSongsToPlaylist() {
try? backgroundContext.save()
Task {
var ids: [NSManagedObjectID]? = nil
await backgroundContext.perform { [weak self] in
guard let self else { return }
ids = backgroundContext.registeredObjects.map {
$0.objectID
}
songsToBeAdded.removeAll()
backgroundContext.reset()
}
//Only add songs that needs to be added to the playlist
if let ids {
PlaylistCreateViewModel.shared.addSongsFromSongAddingView(for: ids)
}
}
}
}
class PlaylistCreateViewModel: ObservableObject {
static let shared = PlaylistCreateViewModel()
private let moc = DataController.shared.container.viewContext
@Published var playlistSongs: [Song] = []
func addSongsFromSongAddingView(for objectIds: [NSManagedObjectID]) async {
objectIds.forEach { id in
if let songToBeAdded = moc.object(with: id) as? Song {
playlistSongs.append(songToBeAdded)
}
}
}
}