From 0a7996ffbc0534ec4755ec0a3d497bf173480655 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 11 May 2017 16:09:39 -0400 Subject: [PATCH] Perform contact intersection ~every 6 hours even if no contacts changed // FREEBIE --- .../ViewControllers/SignalsViewController.m | 6 ++- .../src/contact/SystemContactsFetcher.swift | 47 +++++++++++++++---- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/Signal/src/ViewControllers/SignalsViewController.m b/Signal/src/ViewControllers/SignalsViewController.m index 9214cda39..2b042202b 100644 --- a/Signal/src/ViewControllers/SignalsViewController.m +++ b/Signal/src/ViewControllers/SignalsViewController.m @@ -283,8 +283,10 @@ NSString *const SignalsViewControllerSegueShowIncomingCall = @"ShowIncomingCallS MessageComposeTableViewController *viewController = [MessageComposeTableViewController new]; [self.contactsManager requestSystemContactsOnceWithCompletion:^(NSError *_Nullable error) { - DDLogError(@"%@ Error when requesting contacts: %@", self.tag, error); - // Even if there was an error fetching contacts we proceed to the next screen. + if (error) { + DDLogError(@"%@ Error when requesting contacts: %@", self.tag, error); + } + // Even if there is an error fetching contacts we proceed to the next screen. // As the compose view will present the proper thing depending on contact access. // // We just want to make sure contact access is *complete* before showing the compose diff --git a/Signal/src/contact/SystemContactsFetcher.swift b/Signal/src/contact/SystemContactsFetcher.swift index 808a51fb5..8adc0a5c3 100644 --- a/Signal/src/contact/SystemContactsFetcher.swift +++ b/Signal/src/contact/SystemContactsFetcher.swift @@ -15,6 +15,7 @@ class SystemContactsFetcher: NSObject { private let TAG = "[SystemContactsFetcher]" var lastContactUpdateHash: Int? + var lastDelegateNotificationDate: Date? public weak var delegate: SystemContactsFetcherDelegate? @@ -109,7 +110,7 @@ class SystemContactsFetcher: NSObject { systemContactsHaveBeenRequestedAtLeastOnce = true - DispatchQueue.global().async { + DispatchQueue.default.async { var systemContacts = [CNContact]() do { let contactFetchRequest = CNContactFetchRequest(keysToFetch: self.allowedContactKeys) @@ -126,20 +127,46 @@ class SystemContactsFetcher: NSObject { } let contacts = systemContacts.map { Contact(systemContact: $0) } - let contactsHash = HashableArray(contacts).hashValue - guard (self.lastContactUpdateHash != contactsHash) else { - Logger.debug("\(self.TAG) System contacts unchanged. hash:\(contactsHash)") - DispatchQueue.main.async { + + DispatchQueue.main.async { + var shouldNotifyDelegate = false + + if self.lastContactUpdateHash != contactsHash { + Logger.info("\(self.TAG) contact hash changed. new contactsHash: \(contactsHash)") + shouldNotifyDelegate = true + } else { + + // If nothing has changed, only notify delegate (to perform contact intersection) every N hours + if let lastDelegateNotificationDate = self.lastDelegateNotificationDate { + let kDebounceInterval = TimeInterval(12 * 60 * 60) + + let expiresAtDate = Date(timeInterval: kDebounceInterval, since:lastDelegateNotificationDate) + if Date() > expiresAtDate { + Logger.info("\(self.TAG) debounce interval expired at: \(expiresAtDate)") + shouldNotifyDelegate = true + } else { + Logger.info("\(self.TAG) ignoring since debounce interval hasn't expired") + } + } else { + Logger.info("\(self.TAG) first contact fetch. contactsHash: \(contactsHash)") + shouldNotifyDelegate = true + } + } + + guard shouldNotifyDelegate else { + Logger.info("\(self.TAG) no reason to notify delegate.") + completion?(nil) + + return } - return - } - self.lastContactUpdateHash = contactsHash - Logger.debug("\(self.TAG) Notifying delegate that system contacts did change. hash:\(contactsHash)") + Logger.debug("\(self.TAG) Notifying delegate that system contacts did change. hash:\(contactsHash)") + + self.lastDelegateNotificationDate = Date() + self.lastContactUpdateHash = contactsHash - DispatchQueue.main.async { self.delegate?.systemContactsFetcher(self, updatedContacts: contacts) completion?(nil) }