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.
CodePudding user response:
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) {
let fetchRequest = CNContactFetchRequest(keysToFetch: keys)
do {
try store.enumerateContacts(with: fetchRequest) { contact, _ in
DispatchQueue.main.async {
// item creation
}
}
} catch {
// error handling
}
}
}