ios – Optimizing Contact Knowledge Processing in Swift to Stop UI Freezing


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?

Related Articles

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Latest Articles