diff --git a/Signal/src/contact/SystemContactsFetcher.swift b/Signal/src/contact/SystemContactsFetcher.swift index 29bf1bcba..79cc2ee8f 100644 --- a/Signal/src/contact/SystemContactsFetcher.swift +++ b/Signal/src/contact/SystemContactsFetcher.swift @@ -88,21 +88,30 @@ class SystemContactsFetcher: NSObject { } private func updateContacts() { + AssertIsOnMainThread() + systemContactsHaveBeenRequestedAtLeastOnce = true - var systemContacts = [CNContact]() - do { - let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys) - try contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in - systemContacts.append(contact) + let contactStore = self.contactStore + let allowedContactKeys = self.allowedContactKeys + + DispatchQueue.global().async { + var systemContacts = [CNContact]() + do { + let contactFetchRequest = CNContactFetchRequest(keysToFetch: allowedContactKeys) + try contactStore.enumerateContacts(with: contactFetchRequest) { (contact, _) -> Void in + systemContacts.append(contact) + } + } catch let error as NSError { + Logger.error("\(self.TAG) Failed to fetch contacts with error:\(error)") + assertionFailure() } - } catch let error as NSError { - Logger.error("\(self.TAG) Failed to fetch contacts with error:\(error)") - assertionFailure() - } - let contacts = systemContacts.map { Contact(systemContact: $0) } - self.delegate?.systemContactsFetcher(self, updatedContacts: contacts) + let contacts = systemContacts.map { Contact(systemContact: $0) } + DispatchQueue.main.async { + self.delegate?.systemContactsFetcher(self, updatedContacts: contacts) + } + } } private func startObservingContactChanges() { diff --git a/Signal/src/util/OWSContactsSyncing.m b/Signal/src/util/OWSContactsSyncing.m index d59279a21..175bdf9e2 100644 --- a/Signal/src/util/OWSContactsSyncing.m +++ b/Signal/src/util/OWSContactsSyncing.m @@ -19,6 +19,8 @@ NSString *const kTSStorageManagerOWSContactsSyncingLastMessageKey = @interface OWSContactsSyncing () +@property (nonatomic, readonly) dispatch_queue_t serialQueue; + @property (nonatomic, readonly) OWSContactsManager *contactsManager; @property (nonatomic, readonly) OWSMessageSender *messageSender; @property (nonatomic) BOOL isRequestInFlight; @@ -70,50 +72,57 @@ NSString *const kTSStorageManagerOWSContactsSyncingLastMessageKey = { AssertIsOnMainThread(); - if (self.isRequestInFlight) { - // De-bounce. It's okay if we ignore some new changes; - // `sendSyncContactsMessageIfPossible` is called fairly - // often so we'll sync soon. - return; + if (!self.serialQueue) { + _serialQueue = dispatch_queue_create("org.whispersystems.contacts.syncing", DISPATCH_QUEUE_SERIAL); } - OWSSyncContactsMessage *syncContactsMessage = - [[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager]; - - NSData *messageData = [syncContactsMessage buildPlainTextAttachmentData]; - - NSData *lastMessageData = - [[TSStorageManager sharedManager] objectForKey:kTSStorageManagerOWSContactsSyncingLastMessageKey - inCollection:kTSStorageManagerOWSContactsSyncingCollection]; + dispatch_async(self.serialQueue, ^{ - if (lastMessageData && [lastMessageData isEqual:messageData]) { - // Ignore redundant contacts sync message. - return; - } + if (self.isRequestInFlight) { + // De-bounce. It's okay if we ignore some new changes; + // `sendSyncContactsMessageIfPossible` is called fairly + // often so we'll sync soon. + return; + } - self.isRequestInFlight = YES; + OWSSyncContactsMessage *syncContactsMessage = + [[OWSSyncContactsMessage alloc] initWithContactsManager:self.contactsManager]; - [self.messageSender sendTemporaryAttachmentData:[syncContactsMessage buildPlainTextAttachmentData] - contentType:OWSMimeTypeApplicationOctetStream - inMessage:syncContactsMessage - success:^{ - DDLogInfo(@"%@ Successfully sent contacts sync message.", self.tag); + NSData *messageData = [syncContactsMessage buildPlainTextAttachmentData]; - [[TSStorageManager sharedManager] setObject:messageData - forKey:kTSStorageManagerOWSContactsSyncingLastMessageKey - inCollection:kTSStorageManagerOWSContactsSyncingCollection]; + NSData *lastMessageData = + [[TSStorageManager sharedManager] objectForKey:kTSStorageManagerOWSContactsSyncingLastMessageKey + inCollection:kTSStorageManagerOWSContactsSyncingCollection]; - dispatch_async(dispatch_get_main_queue(), ^{ - self.isRequestInFlight = NO; - }); + if (lastMessageData && [lastMessageData isEqual:messageData]) { + // Ignore redundant contacts sync message. + return; } - failure:^(NSError *error) { - DDLogError(@"%@ Failed to send contacts sync message with error: %@", self.tag, error); - dispatch_async(dispatch_get_main_queue(), ^{ - self.isRequestInFlight = NO; - }); - }]; + self.isRequestInFlight = YES; + + [self.messageSender sendTemporaryAttachmentData:[syncContactsMessage buildPlainTextAttachmentData] + contentType:OWSMimeTypeApplicationOctetStream + inMessage:syncContactsMessage + success:^{ + DDLogInfo(@"%@ Successfully sent contacts sync message.", self.tag); + + [[TSStorageManager sharedManager] setObject:messageData + forKey:kTSStorageManagerOWSContactsSyncingLastMessageKey + inCollection:kTSStorageManagerOWSContactsSyncingCollection]; + + dispatch_async(self.serialQueue, ^{ + self.isRequestInFlight = NO; + }); + } + failure:^(NSError *error) { + DDLogError(@"%@ Failed to send contacts sync message with error: %@", self.tag, error); + + dispatch_async(self.serialQueue, ^{ + self.isRequestInFlight = NO; + }); + }]; + }); } - (void)sendSyncContactsMessageIfPossible