Nie wiem, czy to pytanie kwalifikuje się do tego, czy nie, ale nawet po tak wielu badaniach nie mogłem znaleźć odpowiedniego przewodnika na to pytanie. Mam nadzieję, że otrzymam odpowiedź tutaj.W jaki sposób aplikacje do wysyłania wiadomości iOS, takie jak Viber, Telegram, WhatsApp pobierają kontakty tak szybko i skutecznie
Widzę, że wszystkie aplikacje do przesyłania wiadomości, takie jak Viber, WhatsApp, Telegram, pobierają kontakty użytkownika i analizują je tak szybko i sprawnie, że opóźnienie jest prawie zerowe. Próbowałem to powtórzyć, ale nigdy się nie udało. Zawsze zajmuje dobre 40-60 sekund na przeanalizowanie 3000 kontaktów, wykonując całą operację na wątku tła. Nawet to powoduje zamrożenie interfejsu użytkownika na wolniejszych urządzeniach, takich jak 5 i 5S. Po pobraniu kontaktów muszę wysłać je do zaplecza, aby zidentyfikować, który użytkownik jest zarejestrowany na platformie, co również daje łączny czas. Powyższe aplikacje robią to w mgnieniu oka!
Byłbym szczęśliwy, gdyby ktoś mógł zaproponować sposób na przeanalizowanie kontaktów w najbardziej wydajny i szybszy sposób bez blokowania głównego wątku.
Oto kod, którego używam w tej chwili.
final class CNContactsService: ContactsService {
private let phoneNumberKit = PhoneNumberKit()
private var allContacts:[Contact] = []
private let contactsStore: CNContactStore
init(network:Network) {
contactsStore = CNContactStore()
self.network = network
}
func fetchContacts() {
fetchLocalContacts { (error) in
if let uError = error {
} else {
let contactsArray = self.allContacts
self.checkContacts(contacts: contactsArray, checkCompletion: { (Users) in
let nonUsers = contactsArray.filter { contact in
return !Users.contains(contact)
}
self.Users.value = Users
self.nonUsers.value = nonUsers
})
}
}
}
func fetchLocalContacts(_ completion: @escaping (NSError?) -> Void) {
switch CNContactStore.authorizationStatus(for: CNEntityType.contacts) {
case CNAuthorizationStatus.denied, CNAuthorizationStatus.restricted:
//User has denied the current app to access the contacts.
self.displayNoAccessMsg()
case CNAuthorizationStatus.notDetermined:
//This case means the user is prompted for the first time for allowing contacts
contactsStore.requestAccess(for: CNEntityType.contacts, completionHandler: { (granted, error) -> Void in
//At this point an alert is provided to the user to provide access to contacts. This will get invoked if a user responds to the alert
if (!granted){
DispatchQueue.main.async(execute: {() -> Void in
completion(error as! NSError)
})
} else{
self.fetchLocalContacts(completion)
}
})
case CNAuthorizationStatus.authorized:
//Authorization granted by user for this app.
var contactsArray = [EPContact]()
let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys)
do {
// let phoneNumberKit = PhoneNumberKit()
try self.contactsStore.enumerateContacts(with: contactFetchRequest, usingBlock: { (contact, stop) -> Void in
//Ordering contacts based on alphabets in firstname
if let contactItem = self.contactFrom(contact: contact) {
contactsArray.append(contactItem)
}
})
self.allContacts = contactsArray
completion(nil)
} catch let error as NSError {
print(error.localizedDescription)
completion(error)
}
}
}
private var allowedContactKeys: [CNKeyDescriptor]{
//We have to provide only the keys which we have to access. We should avoid unnecessary keys when fetching the contact. Reducing the keys means faster the access.
return [
CNContactGivenNameKey as CNKeyDescriptor,
CNContactFamilyNameKey as CNKeyDescriptor,
CNContactOrganizationNameKey as CNKeyDescriptor,
CNContactThumbnailImageDataKey as CNKeyDescriptor,
CNContactPhoneNumbersKey as CNKeyDescriptor,
]
}
private func checkUsers(contacts:[Contact],checkCompletion:@escaping ([Contact])->Void) {
let phoneNumbers = contacts.flatMap{$0.phoneNumbers}
if phoneNumbers.isEmpty {
checkCompletion([])
return
}
network.request(.registeredContacts(numbers: phoneNumbersList), completion: { (result) in
switch result {
case .success(let response):
do {
let profiles = try response.map([Profile].self)
let contacts = profiles.map{ CNContactsService.contactFrom(profile: $0) }
checkCompletion(contacts)
} catch {
checkCompletion([])
}
case .failure:
checkCompletion([])
}
})
}
static func contactFrom(profile:Profile) -> Contact {
let firstName = ""
let lastName = ""
let company = ""
var displayName = ""
if let fullName = profile.fullName {
displayName = fullName
} else {
displayName = profile.nickName ?? ""
}
let numbers = [profile.phone!]
if displayName.isEmpty {
displayName = profile.phone!
}
let contactId = String(profile.id)
return Contact(firstName: firstName,
lastName: lastName,
company: company,
displayName: displayName,
thumbnailProfileImage: nil,
contactId: contactId,
phoneNumbers: numbers,
profile: profile)
}
private func parsePhoneNumber(_ number: String) -> String? {
do {
let phoneNumber = try phoneNumberKit.parse(number)
return phoneNumberKit.format(phoneNumber, toType: .e164)
} catch {
return nil
}
}
}`
i styki są pobierane tu, gdy aplikacja jest uruchomiona
private func ApplicationLaunched() {
DispatchQueue.global(qos: .background).async {
let contactsService:ContactsService = self.serviceHolder.get()
contactsService.fetchContacts()
}
Po prostu pytanie, czy próbowałeś grać z 'allowedContactKeys'? Może "CNContactThumbnailImageDataKey" jest zbyt ciężki dla 3000 kontaktów? Nigdy nie próbowałem tego w przypadku wielu kontaktów, ale niemal natychmiast otrzymuję 200 kontaktów w mojej aplikacji, ale nie żądam miniatury. – TawaNicolas
Czy próbowałeś pobrać je wsadowo? – swift2geek
Nie jestem pewien, ale myślę, że WhatsApp rozpoczyna synchronizowanie kontaktów zaraz po otwarciu aplikacji. Przeczytaj ten https://www.quora.com/How-does-the-contacts-sync-work-in-WhatsApp/answer/Jinesh-Soni?srid=RhqE –