I’m engaged on an iOS app the place I fetch all contacts from the telephone and retailer them in an array of ContactModel. After retrieving the contacts, I course of the info to detect duplicates, lacking particulars, and group related names. The operate I exploit for this processing is under:
func compileContactsDataPublisher(contactsArray: [ContactModel]) -> AnyPublisher {
return Simply(contactsArray)
.map { contacts -> ContactsCompilation in
var emailToContacts = [String: [ContactModel]]()
var phoneToContacts = [String: [ContactModel]]()
var contactsWithoutDetails = [ContactModel]()
var nameToContacts = [String: [ContactModel]]()
var fullNameToContacts = [String: [ContactModel]]()
var groupedContacts = [String: [ContactModel]]()
var noPhone = [ContactModel]()
var organizations = [ContactModel]()
var jobTitles = [ContactModel]()
var birthdays = [ContactModel]()
var socialAccounts = [ContactModel]()
for contact in contacts {
// 1. Course of emails and telephones for duplicates
contact.emailAddresses.forEach { electronic mail in
emailToContacts[email, default: []].append(contact)
}
contact.phoneNumbers.forEach { telephone in
phoneToContacts[phone, default: []].append(contact)
}
// 2. Verify for contacts with out particulars
if contact.emailAddresses.isEmpty && contact.phoneNumbers.isEmpty {
contactsWithoutDetails.append(contact)
}
// 3. Group by names for related names test
[contact.givenName, contact.familyName].forEach { identify in
guard !identify.isEmpty else { return }
nameToContacts[name, default: []].append(contact)
}
// 4. Group by full identify for noName test
let fullName = [contact.givenName, contact.familyName].joined(separator: " ").trimmingCharacters(in: .whitespaces)
let fullNameKey = fullName.isEmpty ? "No Title" : fullName
fullNameToContacts[fullNameKey, default: []].append(contact)
// 5. Generate key for precise duplicates
let exactKey = [
contact.givenName,
contact.middleName,
contact.familyName,
contact.nickname,
contact.organizationName,
contact.jobTitle,
contact.departmentName,
contact.phoneNumbers.joined(separator: ","),
contact.emailAddresses.joined(separator: ",")
].joined(separator: "-")
groupedContacts[exactKey, default: []].append(contact)
// 6. Mixed checks
if contact.phoneNumbers.isEmpty {
noPhone.append(contact)
}
if contact.organizationName.rely > 1 {
organizations.append(contact)
}
if contact.jobTitle.rely > 1 {
jobTitles.append(contact)
}
if contact.birthday != nil {
birthdays.append(contact)
}
if !contact.socialProfiles.isEmpty {
socialAccounts.append(contact)
}
}
// Extract duplicates and teams
let duplicateEmails = emailToContacts.values.filter { $0.rely > 1 }
let duplicatePhones = phoneToContacts.values.filter { $0.rely > 1 }
let similarNames = nameToContacts.values.filter { $0.rely > 1 }
let noName = fullNameToContacts.values.filter { $0.rely > 1 }
let duplicates = groupedContacts.values.filter { $0.rely > 1 }
return ContactsCompilation(
duplicateEmails: duplicateEmails,
duplicatePhones: duplicatePhones,
contactsWithoutDetails: contactsWithoutDetails,
similarNames: similarNames,
noName: noName,
duplicates: duplicates,
noPhone: noPhone,
organizations: organizations,
jobTitles: jobTitles,
birthdays: birthdays,
socialAccounts: socialAccounts
)
}
.eraseToAnyPublisher()
}
Problem:
This operate works completely effective with a smaller variety of contacts, however after I check it with 1000 to 2000 contacts, the app turns into unresponsive and takes a very long time to course of the info (a number of seconds).
Nonetheless, I’ve seen related apps that load and course of 2000 contacts inside 3-4 seconds, whereas my implementation takes considerably longer. I think that the difficulty is said to synchronous processing on the primary thread, however I’m not sure methods to optimize it correctly.
What I Have Tried:
- Utilizing Mix: The operate is wrapped inside Simply and .map to course of the contacts asynchronously, however it nonetheless appears to dam the UI.
- Performing Computation in a Background Thread: I attempted wrapping the operation inside a DispatchQueue.world(qos: .userInitiated).async block, however it didn’t enhance efficiency a lot.
- Optimizing Knowledge Constructions: Utilizing dictionaries for lookups as an alternative of arrays to enhance effectivity.
- Lowering String Operations: Minimizing the variety of .joined(separator:) calls.
Questions:
- How can I course of this contact record extra effectively to forestall UI freezing?
- Are there every other Swift efficiency optimizations I can apply to make this course of sooner?