I need to fetch contacts at application launch and store them with Core Data.
For fetching I have following method:
static var fetchAllContacts: PersistenceController = {
let result = PersistenceController(inMemory: true)
let viewContext = result.container.viewContext
// Run this in the background async
// Get access to the Contacts store
let store = CNContactStore()
// Specify which data keys we want to fetch
let keys = [CNContactGivenNameKey, CNContactPhoneNumbersKey] as [CNKeyDescriptor]
DispatchQueue.global(qos: .background).async {
let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
DispatchQueue.main.async {
// Call method to fetch all contacts
do {
try store.enumerateContacts( //This method should not be called on the main thread as it may lead to UI unresponsiveness.
with: fetchRequest,
usingBlock: { contact, _ in
// Do something with the contact
let newItem = Item(context: viewContext)
newItem.id = UUID()
newItem.timestamp = Date()
newItem.givenName = contact.givenName
do {
try viewContext.save()
} catch {
/*
Replace this implementation with code to handle the error appropriately.
fatalError() causes the application to generate a crash log and terminate.
You should not use this function in a shipping application,
although it may be useful during development.
*/
let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
}
}
)
} catch {
// If there was an error, handle it here
print("Error")
}
}
}
return result
}()
However with Xcode 14 I have alert that asks to separate threads.
try store.enumerateContacts( //This method should not be called on the main thread as it may lead to UI unresponsiveness.
How may I move store.enumerateContacts
out of the main thread so that it will fix an alert.
DispatchQueue has multiple QoSClass
priority settings. DispatchQueue.main
has the highest priority, and is typically reserved for UI updates. All other QoSClass
settings can be viewed here: https://developer.apple.com/documentation/dispatch/dispatchqos/qosclass
You mention that the CoreData
logic should be performed at app launch. I'd first suggest changing the outermost DispatchQueue
block to .userInteractive
or .userInitiated
. This gives the operation higher priority, therefore more compute power if available.
Secondly, use DispatchQueue.main
only for UI updates. I assume that Item
is a data object that gets used in a View
immediately? Only run the Item
setup on .main
.
For example:
static var fetchAllContacts: PersistenceController = {
// setup code
DispatchQueue.global(qos: .userInitiated).async {
let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
do {
try store.enumerateContacts(with: fetchRequest) { contact, _ in
DispatchQueue.main.async {
// item creation
}
}
} catch {
// error handling
}
}
}