mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-30 09:21:28 +00:00
New address book implementation
(cherry picked from commit a16a3c393628a3e44a14b0c7196b24205f40c1e4)
This commit is contained in:
parent
681ab8248e
commit
735b497a23
@ -25,6 +25,8 @@ swift_library(
|
|||||||
"//submodules/TextFormat:TextFormat",
|
"//submodules/TextFormat:TextFormat",
|
||||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||||
|
"//submodules/TelegramCore/FlatBuffers",
|
||||||
|
"//submodules/TelegramCore/FlatSerialization",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import Contacts
|
import Contacts
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
|
import FlatBuffers
|
||||||
|
import FlatSerialization
|
||||||
|
|
||||||
public final class DeviceContactPhoneNumberData: Equatable {
|
public final class DeviceContactPhoneNumberData: Equatable {
|
||||||
public let label: String
|
public let label: String
|
||||||
@ -11,6 +13,22 @@ public final class DeviceContactPhoneNumberData: Equatable {
|
|||||||
self.value = value
|
self.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init(flatBuffersObject: TelegramCore_DeviceContactPhoneNumberData) {
|
||||||
|
self.label = flatBuffersObject.label
|
||||||
|
self.value = flatBuffersObject.value
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encodeToFlatBuffers(builder: inout FlatBufferBuilder) -> Offset {
|
||||||
|
let labelOffset = builder.create(string: self.label)
|
||||||
|
let valueOffset = builder.create(string: self.value)
|
||||||
|
|
||||||
|
return TelegramCore_DeviceContactPhoneNumberData.createDeviceContactPhoneNumberData(
|
||||||
|
&builder,
|
||||||
|
labelOffset: labelOffset,
|
||||||
|
valueOffset: valueOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public static func == (lhs: DeviceContactPhoneNumberData, rhs: DeviceContactPhoneNumberData) -> Bool {
|
public static func == (lhs: DeviceContactPhoneNumberData, rhs: DeviceContactPhoneNumberData) -> Bool {
|
||||||
if lhs.label != rhs.label {
|
if lhs.label != rhs.label {
|
||||||
return false
|
return false
|
||||||
@ -216,6 +234,38 @@ public final class DeviceContactBasicData: Equatable {
|
|||||||
self.phoneNumbers = phoneNumbers
|
self.phoneNumbers = phoneNumbers
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public init(flatBuffersObject: TelegramCore_StoredDeviceContactData) {
|
||||||
|
self.firstName = flatBuffersObject.firstName
|
||||||
|
self.lastName = flatBuffersObject.lastName
|
||||||
|
|
||||||
|
if flatBuffersObject.phoneNumbersCount == 1 {
|
||||||
|
self.phoneNumbers = [
|
||||||
|
DeviceContactPhoneNumberData(flatBuffersObject: flatBuffersObject.phoneNumbers(at: 0)!)
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
var phoneNumbers: [DeviceContactPhoneNumberData] = []
|
||||||
|
for i in 0 ..< flatBuffersObject.phoneNumbersCount {
|
||||||
|
phoneNumbers.append(DeviceContactPhoneNumberData(flatBuffersObject: flatBuffersObject.phoneNumbers(at: i)!))
|
||||||
|
}
|
||||||
|
self.phoneNumbers = phoneNumbers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encodeToFlatBuffers(builder: inout FlatBufferBuilder) -> Offset {
|
||||||
|
let phoneNumberOffsets = self.phoneNumbers.map { $0.encodeToFlatBuffers(builder: &builder) }
|
||||||
|
let phoneNumberOffset = builder.createVector(ofOffsets: phoneNumberOffsets, len: phoneNumberOffsets.count)
|
||||||
|
|
||||||
|
let firstNameOffset = builder.create(string: self.firstName)
|
||||||
|
let lastNameOffset = builder.create(string: self.lastName)
|
||||||
|
|
||||||
|
return TelegramCore_StoredDeviceContactData.createStoredDeviceContactData(
|
||||||
|
&builder,
|
||||||
|
firstNameOffset: firstNameOffset,
|
||||||
|
lastNameOffset: lastNameOffset,
|
||||||
|
phoneNumbersVectorOffset: phoneNumberOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
public static func ==(lhs: DeviceContactBasicData, rhs: DeviceContactBasicData) -> Bool {
|
public static func ==(lhs: DeviceContactBasicData, rhs: DeviceContactBasicData) -> Bool {
|
||||||
if lhs.firstName != rhs.firstName {
|
if lhs.firstName != rhs.firstName {
|
||||||
return false
|
return false
|
||||||
@ -230,6 +280,86 @@ public final class DeviceContactBasicData: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final class DeviceContactDataState: Codable {
|
||||||
|
private enum CodingKeys: String, CodingKey {
|
||||||
|
case contactsData
|
||||||
|
case contactsKeys
|
||||||
|
case telegramReferencesKeys
|
||||||
|
case telegramReferencesValues
|
||||||
|
case stateToken
|
||||||
|
}
|
||||||
|
|
||||||
|
public let contacts: [String: DeviceContactBasicData]
|
||||||
|
public let telegramReferences: [EnginePeer.Id: String]
|
||||||
|
public let stateToken: Data?
|
||||||
|
|
||||||
|
public init(contacts: [String: DeviceContactBasicData], telegramReferences: [EnginePeer.Id: String], stateToken: Data?) {
|
||||||
|
self.contacts = contacts
|
||||||
|
self.telegramReferences = telegramReferences
|
||||||
|
self.stateToken = stateToken
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(from decoder: any Decoder) throws {
|
||||||
|
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
let contactsData = try container.decode([Data].self, forKey: .contactsData).map { data in
|
||||||
|
var byteBuffer = ByteBuffer(data: data)
|
||||||
|
let deserializedValue = FlatBuffers_getRoot(byteBuffer: &byteBuffer) as TelegramCore_StoredDeviceContactData
|
||||||
|
let parsedValue = DeviceContactBasicData(flatBuffersObject: deserializedValue)
|
||||||
|
return parsedValue
|
||||||
|
}
|
||||||
|
let contactsKeys = try container.decode([String].self, forKey: .contactsKeys)
|
||||||
|
|
||||||
|
var contacts: [String: DeviceContactBasicData] = [:]
|
||||||
|
for i in 0 ..< min(contactsData.count, contactsKeys.count) {
|
||||||
|
contacts[contactsKeys[i]] = contactsData[i]
|
||||||
|
}
|
||||||
|
self.contacts = contacts
|
||||||
|
|
||||||
|
let telegramReferencesKeys = try container.decode([Int64].self, forKey: .telegramReferencesKeys).map { value in
|
||||||
|
return EnginePeer.Id(value)
|
||||||
|
}
|
||||||
|
let telegramReferencesValues = try container.decode([String].self, forKey: .telegramReferencesValues)
|
||||||
|
|
||||||
|
var telegramReferences: [EnginePeer.Id: String] = [:]
|
||||||
|
for i in 0 ..< min(telegramReferencesValues.count, telegramReferencesKeys.count) {
|
||||||
|
telegramReferences[telegramReferencesKeys[i]] = telegramReferencesValues[i]
|
||||||
|
}
|
||||||
|
self.telegramReferences = telegramReferences
|
||||||
|
|
||||||
|
self.stateToken = try container.decodeIfPresent(Data.self, forKey: .stateToken)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(to encoder: any Encoder) throws {
|
||||||
|
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||||
|
|
||||||
|
var contactsData: [Data] = []
|
||||||
|
var contactsKeys: [String] = []
|
||||||
|
for (key, contact) in self.contacts {
|
||||||
|
var builder = FlatBufferBuilder(initialSize: 1024)
|
||||||
|
let offset = contact.encodeToFlatBuffers(builder: &builder)
|
||||||
|
builder.finish(offset: offset)
|
||||||
|
contactsData.append(builder.data)
|
||||||
|
contactsKeys.append(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
var telegramReferencesKeys: [Int64] = []
|
||||||
|
var telegramReferencesValues: [String] = []
|
||||||
|
for (key, value) in self.telegramReferences {
|
||||||
|
telegramReferencesKeys.append(key.toInt64())
|
||||||
|
telegramReferencesValues.append(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
try container.encode(contactsKeys, forKey: .contactsKeys)
|
||||||
|
try container.encode(contactsData, forKey: .contactsData)
|
||||||
|
|
||||||
|
try container.encode(telegramReferencesKeys, forKey: .telegramReferencesKeys)
|
||||||
|
try container.encode(telegramReferencesValues, forKey: .telegramReferencesValues)
|
||||||
|
|
||||||
|
try container.encodeIfPresent(stateToken, forKey: .stateToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final class DeviceContactBasicDataWithReference: Equatable {
|
public final class DeviceContactBasicDataWithReference: Equatable {
|
||||||
public let stableId: DeviceContactStableId
|
public let stableId: DeviceContactStableId
|
||||||
public let basicData: DeviceContactBasicData
|
public let basicData: DeviceContactBasicData
|
||||||
@ -327,17 +457,12 @@ public final class DeviceContactExtendedData: Equatable {
|
|||||||
|
|
||||||
public extension DeviceContactExtendedData {
|
public extension DeviceContactExtendedData {
|
||||||
convenience init?(vcard: Data) {
|
convenience init?(vcard: Data) {
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
guard let contact = (try? CNContactVCardSerialization.contacts(with: vcard))?.first else {
|
||||||
guard let contact = (try? CNContactVCardSerialization.contacts(with: vcard))?.first else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
self.init(contact: contact)
|
|
||||||
} else {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
self.init(contact: contact)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
|
||||||
func asMutableCNContact() -> CNMutableContact {
|
func asMutableCNContact() -> CNMutableContact {
|
||||||
let contact = CNMutableContact()
|
let contact = CNMutableContact()
|
||||||
contact.givenName = self.basicData.firstName
|
contact.givenName = self.basicData.firstName
|
||||||
@ -385,7 +510,6 @@ public extension DeviceContactExtendedData {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
|
||||||
convenience init(contact: CNContact) {
|
convenience init(contact: CNContact) {
|
||||||
var phoneNumbers: [DeviceContactPhoneNumberData] = []
|
var phoneNumbers: [DeviceContactPhoneNumberData] = []
|
||||||
for number in contact.phoneNumbers {
|
for number in contact.phoneNumbers {
|
||||||
|
@ -5,6 +5,8 @@ import SwiftSignalKit
|
|||||||
|
|
||||||
public typealias DeviceContactStableId = String
|
public typealias DeviceContactStableId = String
|
||||||
|
|
||||||
|
public var sharedDisableDeviceContactDataDiffing: Bool = false
|
||||||
|
|
||||||
public protocol DeviceContactDataManager: AnyObject {
|
public protocol DeviceContactDataManager: AnyObject {
|
||||||
func personNameDisplayOrder() -> Signal<PresentationPersonNameOrder, NoError>
|
func personNameDisplayOrder() -> Signal<PresentationPersonNameOrder, NoError>
|
||||||
func basicData() -> Signal<[DeviceContactStableId: DeviceContactBasicData], NoError>
|
func basicData() -> Signal<[DeviceContactStableId: DeviceContactBasicData], NoError>
|
||||||
|
22
submodules/ContactsHelper/BUILD
Normal file
22
submodules/ContactsHelper/BUILD
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
objc_library(
|
||||||
|
name = "ContactsHelper",
|
||||||
|
enable_modules = True,
|
||||||
|
module_name = "ContactsHelper",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.m",
|
||||||
|
"Sources/**/*.h",
|
||||||
|
], allow_empty = True),
|
||||||
|
hdrs = glob([
|
||||||
|
"PublicHeaders/**/*.h",
|
||||||
|
]),
|
||||||
|
includes = [
|
||||||
|
"PublicHeaders",
|
||||||
|
],
|
||||||
|
sdk_frameworks = [
|
||||||
|
"Foundation",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
30
submodules/ContactsHelper/PublicHeaders/ContactsHelper/ContactsHelper.h
Executable file
30
submodules/ContactsHelper/PublicHeaders/ContactsHelper/ContactsHelper.h
Executable file
@ -0,0 +1,30 @@
|
|||||||
|
#ifndef ContactsHelper_h
|
||||||
|
#define ContactsHelper_h
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Contacts/Contacts.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ContactsEnumerateChangeResult : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSData * _Nullable stateToken;
|
||||||
|
|
||||||
|
- (instancetype)initWithStateToken:(NSData *)stateToken;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface ContactsEnumerateResult : NSObject
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSData * _Nullable stateToken;
|
||||||
|
|
||||||
|
- (instancetype)initWithStateToken:(NSData *)stateToken;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
ContactsEnumerateChangeResult * _Nullable ContactsEnumerateChangeRequest(CNContactStore *store, CNChangeHistoryFetchRequest *fetchRequest, id<CNChangeHistoryEventVisitor> visitor);
|
||||||
|
ContactsEnumerateResult * _Nullable ContactsEnumerateRequest(CNContactStore *store, CNContactFetchRequest *fetchRequest, void (^onContact)(CNContact *));
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
||||||
|
#endif /* ContactsHelper_h */
|
46
submodules/ContactsHelper/Sources/ContactsHelper.m
Executable file
46
submodules/ContactsHelper/Sources/ContactsHelper.m
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
#import <ContactsHelper/ContactsHelper.h>
|
||||||
|
|
||||||
|
@implementation ContactsEnumerateChangeResult
|
||||||
|
|
||||||
|
- (instancetype)initWithStateToken:(NSData *)stateToken {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_stateToken = stateToken;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ContactsEnumerateResult
|
||||||
|
|
||||||
|
- (instancetype)initWithStateToken:(NSData *)stateToken {
|
||||||
|
self = [super init];
|
||||||
|
if (self != nil) {
|
||||||
|
_stateToken = stateToken;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
ContactsEnumerateChangeResult * _Nullable ContactsEnumerateChangeRequest(CNContactStore *store, CNChangeHistoryFetchRequest *fetchRequest, id<CNChangeHistoryEventVisitor> visitor) {
|
||||||
|
NSError *error = nil;
|
||||||
|
CNFetchResult<NSEnumerator<CNChangeHistoryEvent *> *> *fetchResult = [store enumeratorForChangeHistoryFetchRequest:fetchRequest error:&error];
|
||||||
|
|
||||||
|
for (CNChangeHistoryEvent *event in fetchResult.value) {
|
||||||
|
[event acceptEventVisitor:visitor];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[ContactsEnumerateChangeResult alloc] initWithStateToken:fetchResult.currentHistoryToken];
|
||||||
|
}
|
||||||
|
|
||||||
|
ContactsEnumerateResult * _Nullable ContactsEnumerateRequest(CNContactStore *store, CNContactFetchRequest *fetchRequest, void (^onContact)(CNContact *)) {
|
||||||
|
NSError *error = nil;
|
||||||
|
CNFetchResult<NSEnumerator<CNContact *> *> *fetchResult = [store enumeratorForContactFetchRequest:fetchRequest error:&error];
|
||||||
|
for (CNContact *contact in fetchResult.value) {
|
||||||
|
onContact(contact);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [[ContactsEnumerateResult alloc] initWithStateToken:fetchResult.currentHistoryToken];
|
||||||
|
}
|
@ -7,7 +7,6 @@ import SwiftSignalKit
|
|||||||
import Photos
|
import Photos
|
||||||
import CoreLocation
|
import CoreLocation
|
||||||
import Contacts
|
import Contacts
|
||||||
import AddressBook
|
|
||||||
import UserNotifications
|
import UserNotifications
|
||||||
import CoreTelephony
|
import CoreTelephony
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
@ -155,29 +154,17 @@ public final class DeviceAccess {
|
|||||||
}
|
}
|
||||||
case .contacts:
|
case .contacts:
|
||||||
let status = Signal<AccessType, NoError> { subscriber in
|
let status = Signal<AccessType, NoError> { subscriber in
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
switch CNContactStore.authorizationStatus(for: .contacts) {
|
||||||
switch CNContactStore.authorizationStatus(for: .contacts) {
|
case .notDetermined:
|
||||||
case .notDetermined:
|
subscriber.putNext(.notDetermined)
|
||||||
subscriber.putNext(.notDetermined)
|
case .authorized:
|
||||||
case .authorized:
|
subscriber.putNext(.allowed)
|
||||||
subscriber.putNext(.allowed)
|
case .limited:
|
||||||
case .limited:
|
subscriber.putNext(.limited)
|
||||||
subscriber.putNext(.limited)
|
default:
|
||||||
default:
|
subscriber.putNext(.denied)
|
||||||
subscriber.putNext(.denied)
|
|
||||||
}
|
|
||||||
subscriber.putCompletion()
|
|
||||||
} else {
|
|
||||||
switch ABAddressBookGetAuthorizationStatus() {
|
|
||||||
case .notDetermined:
|
|
||||||
subscriber.putNext(.notDetermined)
|
|
||||||
case .authorized:
|
|
||||||
subscriber.putNext(.allowed)
|
|
||||||
default:
|
|
||||||
subscriber.putNext(.denied)
|
|
||||||
}
|
|
||||||
subscriber.putCompletion()
|
|
||||||
}
|
}
|
||||||
|
subscriber.putCompletion()
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
}
|
}
|
||||||
return status
|
return status
|
||||||
@ -531,47 +518,22 @@ public final class DeviceAccess {
|
|||||||
if let value = value {
|
if let value = value {
|
||||||
completion(value)
|
completion(value)
|
||||||
} else {
|
} else {
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
switch CNContactStore.authorizationStatus(for: .contacts) {
|
||||||
switch CNContactStore.authorizationStatus(for: .contacts) {
|
case .notDetermined:
|
||||||
case .notDetermined:
|
let store = CNContactStore()
|
||||||
let store = CNContactStore()
|
store.requestAccess(for: .contacts, completionHandler: { authorized, _ in
|
||||||
store.requestAccess(for: .contacts, completionHandler: { authorized, _ in
|
self.contactsPromise.set(.single(authorized))
|
||||||
self.contactsPromise.set(.single(authorized))
|
completion(authorized)
|
||||||
completion(authorized)
|
})
|
||||||
})
|
case .authorized:
|
||||||
case .authorized:
|
self.contactsPromise.set(.single(true))
|
||||||
self.contactsPromise.set(.single(true))
|
completion(true)
|
||||||
completion(true)
|
case .limited:
|
||||||
case .limited:
|
self.contactsPromise.set(.single(true))
|
||||||
self.contactsPromise.set(.single(true))
|
completion(true)
|
||||||
completion(true)
|
default:
|
||||||
default:
|
self.contactsPromise.set(.single(false))
|
||||||
self.contactsPromise.set(.single(false))
|
completion(false)
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
switch ABAddressBookGetAuthorizationStatus() {
|
|
||||||
case .notDetermined:
|
|
||||||
var error: Unmanaged<CFError>?
|
|
||||||
let addressBook = ABAddressBookCreateWithOptions(nil, &error)
|
|
||||||
if let addressBook = addressBook?.takeUnretainedValue() {
|
|
||||||
ABAddressBookRequestAccessWithCompletion(addressBook, { authorized, _ in
|
|
||||||
Queue.mainQueue().async {
|
|
||||||
self.contactsPromise.set(.single(authorized))
|
|
||||||
completion(authorized)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
self.contactsPromise.set(.single(false))
|
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
case .authorized:
|
|
||||||
self.contactsPromise.set(.single(true))
|
|
||||||
completion(true)
|
|
||||||
default:
|
|
||||||
self.contactsPromise.set(.single(false))
|
|
||||||
completion(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
namespace TelegramCore;
|
||||||
|
|
||||||
|
table DeviceContactPhoneNumberData {
|
||||||
|
label:string (id: 0, required);
|
||||||
|
value:string (id: 1, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
table StoredDeviceContactData {
|
||||||
|
firstName:string (id: 0, required);
|
||||||
|
lastName:string (id: 1, required);
|
||||||
|
phoneNumbers:[DeviceContactPhoneNumberData] (id: 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
root_type StoredDeviceContactData;
|
@ -584,6 +584,7 @@ private enum SharedDataKeyValues: Int32 {
|
|||||||
case countriesList = 7
|
case countriesList = 7
|
||||||
case wallapersState = 8
|
case wallapersState = 8
|
||||||
case chatThemes = 10
|
case chatThemes = 10
|
||||||
|
case deviceContacts = 11
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct SharedDataKeys {
|
public struct SharedDataKeys {
|
||||||
@ -640,6 +641,12 @@ public struct SharedDataKeys {
|
|||||||
key.setInt32(0, value: SharedDataKeyValues.chatThemes.rawValue)
|
key.setInt32(0, value: SharedDataKeyValues.chatThemes.rawValue)
|
||||||
return key
|
return key
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
public static let deviceContacts: ValueBoxKey = {
|
||||||
|
let key = ValueBoxKey(length: 4)
|
||||||
|
key.setInt32(0, value: SharedDataKeyValues.deviceContacts.rawValue)
|
||||||
|
return key
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 {
|
public func applicationSpecificItemCacheCollectionId(_ value: Int8) -> Int8 {
|
||||||
|
@ -4,7 +4,6 @@ import SwiftSignalKit
|
|||||||
import Postbox
|
import Postbox
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import Contacts
|
import Contacts
|
||||||
import AddressBook
|
|
||||||
import Display
|
import Display
|
||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import AppBundle
|
import AppBundle
|
||||||
@ -201,19 +200,11 @@ private func currentDateTimeFormat() -> PresentationDateTimeFormat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func currentPersonNameSortOrder() -> PresentationPersonNameOrder {
|
private func currentPersonNameSortOrder() -> PresentationPersonNameOrder {
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
switch CNContactsUserDefaults.shared().sortOrder {
|
||||||
switch CNContactsUserDefaults.shared().sortOrder {
|
case .givenName:
|
||||||
case .givenName:
|
|
||||||
return .firstLast
|
|
||||||
default:
|
|
||||||
return .lastFirst
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if ABPersonGetSortOrdering() == kABPersonSortByFirstName {
|
|
||||||
return .firstLast
|
return .firstLast
|
||||||
} else {
|
default:
|
||||||
return .lastFirst
|
return .lastFirst
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,6 +482,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/ComposeTodoScreen",
|
"//submodules/TelegramUI/Components/ComposeTodoScreen",
|
||||||
"//submodules/TelegramUI/Components/SuggestedPostApproveAlert",
|
"//submodules/TelegramUI/Components/SuggestedPostApproveAlert",
|
||||||
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
"//submodules/TelegramUI/Components/Stars/BalanceNeededScreen",
|
||||||
|
"//submodules/ContactsHelper",
|
||||||
] + select({
|
] + select({
|
||||||
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,
|
||||||
"//build-system:ios_sim_arm64": [],
|
"//build-system:ios_sim_arm64": [],
|
||||||
|
@ -359,6 +359,10 @@ public final class AccountContextImpl: AccountContext {
|
|||||||
self.appConfigurationDisposable = (self._appConfiguration.get()
|
self.appConfigurationDisposable = (self._appConfiguration.get()
|
||||||
|> deliverOnMainQueue).start(next: { value in
|
|> deliverOnMainQueue).start(next: { value in
|
||||||
let _ = currentAppConfiguration.swap(value)
|
let _ = currentAppConfiguration.swap(value)
|
||||||
|
|
||||||
|
if let data = appConfiguration.data, data["ios_killswitch_contact_diffing"] != nil {
|
||||||
|
sharedDisableDeviceContactDataDiffing = true
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
let langCode = sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode
|
let langCode = sharedContext.currentPresentationData.with { $0 }.strings.baseLanguageCode
|
||||||
|
@ -7,6 +7,8 @@ import TelegramUIPreferences
|
|||||||
import DeviceAccess
|
import DeviceAccess
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
|
import ContactsHelper
|
||||||
|
import AccountContext
|
||||||
|
|
||||||
private protocol DeviceContactDataContext {
|
private protocol DeviceContactDataContext {
|
||||||
func personNameDisplayOrder() -> PresentationPersonNameOrder
|
func personNameDisplayOrder() -> PresentationPersonNameOrder
|
||||||
@ -17,62 +19,222 @@ private protocol DeviceContactDataContext {
|
|||||||
func deleteContactWithAppSpecificReference(peerId: PeerId)
|
func deleteContactWithAppSpecificReference(peerId: PeerId)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 9.0, iOS 9.0, *)
|
|
||||||
private final class DeviceContactDataModernContext: DeviceContactDataContext {
|
private final class DeviceContactDataModernContext: DeviceContactDataContext {
|
||||||
|
let queue: Queue
|
||||||
|
let accountManager: AccountManager<TelegramAccountManagerTypes>
|
||||||
let store = CNContactStore()
|
let store = CNContactStore()
|
||||||
var updateHandle: NSObjectProtocol?
|
var updateHandle: NSObjectProtocol?
|
||||||
var currentContacts: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
var currentContacts: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
||||||
var currentAppSpecificReferences: [PeerId: DeviceContactBasicDataWithReference] = [:]
|
var currentAppSpecificReferences: [PeerId: DeviceContactBasicDataWithReference] = [:]
|
||||||
|
|
||||||
init(queue: Queue, updated: @escaping ([DeviceContactStableId: DeviceContactBasicData]) -> Void, appSpecificReferencesUpdated: @escaping ([PeerId: DeviceContactBasicDataWithReference]) -> Void) {
|
private var retrieveContactsDisposable: Disposable?
|
||||||
let (contacts, references) = self.retrieveContacts()
|
|
||||||
self.currentContacts = contacts
|
init(queue: Queue, accountManager: AccountManager<TelegramAccountManagerTypes>, updated: @escaping ([DeviceContactStableId: DeviceContactBasicData]) -> Void, appSpecificReferencesUpdated: @escaping ([PeerId: DeviceContactBasicDataWithReference]) -> Void) {
|
||||||
self.currentAppSpecificReferences = references
|
self.queue = queue
|
||||||
updated(self.currentContacts)
|
self.accountManager = accountManager
|
||||||
appSpecificReferencesUpdated(self.currentAppSpecificReferences)
|
|
||||||
let handle = NotificationCenter.default.addObserver(forName: NSNotification.Name.CNContactStoreDidChange, object: nil, queue: nil, using: { [weak self] _ in
|
self.retrieveContactsDisposable?.dispose()
|
||||||
queue.async {
|
self.retrieveContactsDisposable = (self.retrieveContacts()
|
||||||
guard let strongSelf = self else {
|
|> deliverOn(self.queue)).startStrict(next: { [weak self] contacts, references in
|
||||||
return
|
guard let self else {
|
||||||
}
|
return
|
||||||
let (contacts, references) = strongSelf.retrieveContacts()
|
|
||||||
if strongSelf.currentContacts != contacts {
|
|
||||||
strongSelf.currentContacts = contacts
|
|
||||||
updated(strongSelf.currentContacts)
|
|
||||||
}
|
|
||||||
if strongSelf.currentAppSpecificReferences != references {
|
|
||||||
strongSelf.currentAppSpecificReferences = references
|
|
||||||
appSpecificReferencesUpdated(strongSelf.currentAppSpecificReferences)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.currentContacts = contacts
|
||||||
|
self.currentAppSpecificReferences = references
|
||||||
|
updated(self.currentContacts)
|
||||||
|
appSpecificReferencesUpdated(self.currentAppSpecificReferences)
|
||||||
|
let handle = NotificationCenter.default.addObserver(forName: NSNotification.Name.CNContactStoreDidChange, object: nil, queue: nil, using: { [weak self] _ in
|
||||||
|
queue.async {
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.retrieveContactsDisposable?.dispose()
|
||||||
|
self.retrieveContactsDisposable = (self.retrieveContacts()
|
||||||
|
|> deliverOn(self.queue)).startStrict(next: { [weak self] contacts, references in
|
||||||
|
guard let self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.currentContacts != contacts {
|
||||||
|
self.currentContacts = contacts
|
||||||
|
updated(self.currentContacts)
|
||||||
|
}
|
||||||
|
if self.currentAppSpecificReferences != references {
|
||||||
|
self.currentAppSpecificReferences = references
|
||||||
|
appSpecificReferencesUpdated(self.currentAppSpecificReferences)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
self.updateHandle = handle
|
||||||
})
|
})
|
||||||
self.updateHandle = handle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
if let updateHandle = updateHandle {
|
self.retrieveContactsDisposable?.dispose()
|
||||||
|
if let updateHandle = self.updateHandle {
|
||||||
NotificationCenter.default.removeObserver(updateHandle)
|
NotificationCenter.default.removeObserver(updateHandle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func retrieveContacts() -> ([DeviceContactStableId: DeviceContactBasicData], [PeerId: DeviceContactBasicDataWithReference]) {
|
private func retrieveContacts() -> Signal<([DeviceContactStableId: DeviceContactBasicData], [PeerId: DeviceContactBasicDataWithReference]), NoError> {
|
||||||
let keysToFetch: [CNKeyDescriptor] = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName), CNContactPhoneNumbersKey as CNKeyDescriptor, CNContactUrlAddressesKey as CNKeyDescriptor]
|
return self.accountManager.transaction { transaction -> DeviceContactDataState? in
|
||||||
|
return transaction.getSharedData(SharedDataKeys.deviceContacts)?.get(DeviceContactDataState.self)
|
||||||
|
}
|
||||||
|
|> deliverOn(self.queue)
|
||||||
|
|> map { currentData -> ([DeviceContactStableId: DeviceContactBasicData], [PeerId: DeviceContactBasicDataWithReference]) in
|
||||||
|
if let currentData, let stateToken = currentData.stateToken, !sharedDisableDeviceContactDataDiffing {
|
||||||
|
final class ChangeVisitor: NSObject, CNChangeHistoryEventVisitor {
|
||||||
|
let onDropEverything: () -> Void
|
||||||
|
let onAdd: (CNContact) -> Void
|
||||||
|
let onUpdate: (CNContact) -> Void
|
||||||
|
let onDelete: (String) -> Void
|
||||||
|
|
||||||
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
|
init(onDropEverything: @escaping () -> Void, onAdd: @escaping (CNContact) -> Void, onUpdate: @escaping (CNContact) -> Void, onDelete: @escaping (String) -> Void) {
|
||||||
request.unifyResults = true
|
self.onDropEverything = onDropEverything
|
||||||
|
self.onAdd = onAdd
|
||||||
|
self.onUpdate = onUpdate
|
||||||
|
self.onDelete = onDelete
|
||||||
|
|
||||||
var result: [DeviceContactStableId: DeviceContactBasicData] = [:]
|
super.init()
|
||||||
var references: [PeerId: DeviceContactBasicDataWithReference] = [:]
|
}
|
||||||
let _ = try? self.store.enumerateContacts(with: request, usingBlock: { contact, _ in
|
|
||||||
let stableIdAndContact = DeviceContactDataModernContext.parseContact(contact)
|
func visit(_ event: CNChangeHistoryDropEverythingEvent) {
|
||||||
result[stableIdAndContact.0] = stableIdAndContact.1
|
self.onDropEverything()
|
||||||
for address in contact.urlAddresses {
|
}
|
||||||
if address.label == "Telegram", let peerId = parseAppSpecificContactReference(address.value as String) {
|
|
||||||
references[peerId] = DeviceContactBasicDataWithReference(stableId: stableIdAndContact.0, basicData: stableIdAndContact.1)
|
func visit(_ event: CNChangeHistoryAddContactEvent) {
|
||||||
|
self.onAdd(event.contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit(_ event: CNChangeHistoryUpdateContactEvent) {
|
||||||
|
self.onUpdate(event.contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
func visit(_ event: CNChangeHistoryDeleteContactEvent) {
|
||||||
|
self.onDelete(event.contactIdentifier)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let changeRequest = CNChangeHistoryFetchRequest()
|
||||||
|
changeRequest.startingToken = stateToken
|
||||||
|
changeRequest.additionalContactKeyDescriptors = [
|
||||||
|
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
|
||||||
|
CNContactPhoneNumbersKey as CNKeyDescriptor,
|
||||||
|
CNContactUrlAddressesKey as CNKeyDescriptor
|
||||||
|
]
|
||||||
|
|
||||||
|
var contacts: [DeviceContactStableId: DeviceContactBasicData] = currentData.contacts
|
||||||
|
var telegramReferences: [PeerId: String] = currentData.telegramReferences
|
||||||
|
var reverseTelegramReferences: [String: Set<PeerId>] = [:]
|
||||||
|
for (peerId, id) in telegramReferences {
|
||||||
|
if reverseTelegramReferences[id] == nil {
|
||||||
|
reverseTelegramReferences[id] = Set()
|
||||||
|
}
|
||||||
|
reverseTelegramReferences[id]?.insert(peerId)
|
||||||
|
}
|
||||||
|
|
||||||
|
let visitor = ChangeVisitor(
|
||||||
|
onDropEverything: {
|
||||||
|
contacts.removeAll()
|
||||||
|
telegramReferences.removeAll()
|
||||||
|
reverseTelegramReferences.removeAll()
|
||||||
|
},
|
||||||
|
onAdd: { contact in
|
||||||
|
let stableIdAndContact = DeviceContactDataModernContext.parseContact(contact)
|
||||||
|
contacts[stableIdAndContact.0] = stableIdAndContact.1
|
||||||
|
for address in contact.urlAddresses {
|
||||||
|
if address.label == "Telegram", let peerId = parseAppSpecificContactReference(address.value as String) {
|
||||||
|
telegramReferences[peerId] = stableIdAndContact.0
|
||||||
|
if reverseTelegramReferences[stableIdAndContact.0] == nil {
|
||||||
|
reverseTelegramReferences[stableIdAndContact.0] = Set()
|
||||||
|
}
|
||||||
|
reverseTelegramReferences[stableIdAndContact.0]?.insert(peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onUpdate: { contact in
|
||||||
|
let stableIdAndContact = DeviceContactDataModernContext.parseContact(contact)
|
||||||
|
contacts[stableIdAndContact.0] = stableIdAndContact.1
|
||||||
|
for address in contact.urlAddresses {
|
||||||
|
if address.label == "Telegram", let peerId = parseAppSpecificContactReference(address.value as String) {
|
||||||
|
telegramReferences[peerId] = stableIdAndContact.0
|
||||||
|
telegramReferences[peerId] = stableIdAndContact.0
|
||||||
|
if reverseTelegramReferences[stableIdAndContact.0] == nil {
|
||||||
|
reverseTelegramReferences[stableIdAndContact.0] = Set()
|
||||||
|
}
|
||||||
|
reverseTelegramReferences[stableIdAndContact.0]?.insert(peerId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDelete: { contactId in
|
||||||
|
contacts.removeValue(forKey: contactId)
|
||||||
|
if let peerIds = reverseTelegramReferences[contactId] {
|
||||||
|
for peerId in peerIds {
|
||||||
|
telegramReferences.removeValue(forKey: peerId)
|
||||||
|
}
|
||||||
|
reverseTelegramReferences.removeValue(forKey: contactId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let resultState = ContactsEnumerateChangeRequest(self.store, changeRequest, visitor)
|
||||||
|
|
||||||
|
let _ = self.accountManager.transaction({ transaction -> Void in
|
||||||
|
transaction.updateSharedData(SharedDataKeys.deviceContacts, { _ in
|
||||||
|
return PreferencesEntry(DeviceContactDataState(
|
||||||
|
contacts: contacts, telegramReferences: telegramReferences, stateToken: resultState?.stateToken))
|
||||||
|
})
|
||||||
|
}).startStandalone()
|
||||||
|
|
||||||
|
var references: [PeerId: DeviceContactBasicDataWithReference] = [:]
|
||||||
|
for (peerId, id) in telegramReferences {
|
||||||
|
if let basicData = contacts[id] {
|
||||||
|
references[peerId] = DeviceContactBasicDataWithReference(stableId: id, basicData: basicData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (contacts, references)
|
||||||
|
} else {
|
||||||
|
let keysToFetch: [CNKeyDescriptor] = [
|
||||||
|
CNContactFormatter.descriptorForRequiredKeys(for: .fullName),
|
||||||
|
CNContactPhoneNumbersKey as CNKeyDescriptor,
|
||||||
|
CNContactUrlAddressesKey as CNKeyDescriptor
|
||||||
|
]
|
||||||
|
|
||||||
|
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
|
||||||
|
request.unifyResults = true
|
||||||
|
|
||||||
|
var contacts: [String: DeviceContactBasicData] = [:]
|
||||||
|
var telegramReferences: [EnginePeer.Id: String] = [:]
|
||||||
|
let resultState = ContactsEnumerateRequest(self.store, request, { contact in
|
||||||
|
let stableIdAndContact = DeviceContactDataModernContext.parseContact(contact)
|
||||||
|
contacts[stableIdAndContact.0] = stableIdAndContact.1
|
||||||
|
for address in contact.urlAddresses {
|
||||||
|
if address.label == "Telegram", let peerId = parseAppSpecificContactReference(address.value as String) {
|
||||||
|
telegramReferences[peerId] = stableIdAndContact.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
let _ = self.accountManager.transaction({ transaction -> Void in
|
||||||
|
transaction.updateSharedData(SharedDataKeys.deviceContacts, { _ in
|
||||||
|
return PreferencesEntry(DeviceContactDataState(
|
||||||
|
contacts: contacts, telegramReferences: telegramReferences, stateToken: resultState?.stateToken))
|
||||||
|
})
|
||||||
|
}).startStandalone()
|
||||||
|
|
||||||
|
var references: [PeerId: DeviceContactBasicDataWithReference] = [:]
|
||||||
|
for (peerId, id) in telegramReferences {
|
||||||
|
if let basicData = contacts[id] {
|
||||||
|
references[peerId] = DeviceContactBasicDataWithReference(stableId: id, basicData: basicData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (contacts, references)
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
return (result, references)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func parseContact(_ contact: CNContact) -> (DeviceContactStableId, DeviceContactBasicData) {
|
private static func parseContact(_ contact: CNContact) -> (DeviceContactStableId, DeviceContactBasicData) {
|
||||||
@ -323,6 +485,7 @@ private final class BasicDataForNormalizedNumberContext {
|
|||||||
|
|
||||||
private final class DeviceContactDataManagerPrivateImpl {
|
private final class DeviceContactDataManagerPrivateImpl {
|
||||||
private let queue: Queue
|
private let queue: Queue
|
||||||
|
private let accountManager: AccountManager<TelegramAccountManagerTypes>
|
||||||
|
|
||||||
private var accessInitialized = false
|
private var accessInitialized = false
|
||||||
|
|
||||||
@ -345,8 +508,9 @@ private final class DeviceContactDataManagerPrivateImpl {
|
|||||||
private let importableContactsSubscribers = Bag<([DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData]) -> Void>()
|
private let importableContactsSubscribers = Bag<([DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData]) -> Void>()
|
||||||
private let appSpecificReferencesSubscribers = Bag<([PeerId: DeviceContactBasicDataWithReference]) -> Void>()
|
private let appSpecificReferencesSubscribers = Bag<([PeerId: DeviceContactBasicDataWithReference]) -> Void>()
|
||||||
|
|
||||||
init(queue: Queue) {
|
init(queue: Queue, accountManager: AccountManager<TelegramAccountManagerTypes>) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
self.accountManager = accountManager
|
||||||
self.accessDisposable = (DeviceAccess.authorizationStatus(subject: .contacts)
|
self.accessDisposable = (DeviceAccess.authorizationStatus(subject: .contacts)
|
||||||
|> delay(2.0, queue: .mainQueue())
|
|> delay(2.0, queue: .mainQueue())
|
||||||
|> deliverOn(self.queue)).startStrict(next: { [weak self] authorizationStatus in
|
|> deliverOn(self.queue)).startStrict(next: { [weak self] authorizationStatus in
|
||||||
@ -355,7 +519,7 @@ private final class DeviceContactDataManagerPrivateImpl {
|
|||||||
}
|
}
|
||||||
strongSelf.accessInitialized = true
|
strongSelf.accessInitialized = true
|
||||||
if authorizationStatus == .allowed {
|
if authorizationStatus == .allowed {
|
||||||
let dataContext = DeviceContactDataModernContext(queue: strongSelf.queue, updated: { stableIdToBasicContactData in
|
let dataContext = DeviceContactDataModernContext(queue: strongSelf.queue, accountManager: strongSelf.accountManager, updated: { stableIdToBasicContactData in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -605,10 +769,10 @@ public final class DeviceContactDataManagerImpl: DeviceContactDataManager {
|
|||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
private let impl: QueueLocalObject<DeviceContactDataManagerPrivateImpl>
|
private let impl: QueueLocalObject<DeviceContactDataManagerPrivateImpl>
|
||||||
|
|
||||||
init() {
|
init(accountManager: AccountManager<TelegramAccountManagerTypes>) {
|
||||||
let queue = self.queue
|
let queue = self.queue
|
||||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||||
return DeviceContactDataManagerPrivateImpl(queue: queue)
|
return DeviceContactDataManagerPrivateImpl(queue: queue, accountManager: accountManager)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,7 +316,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
if applicationBindings.isMainApp {
|
if applicationBindings.isMainApp {
|
||||||
self.locationManager = DeviceLocationManager(queue: Queue.mainQueue())
|
self.locationManager = DeviceLocationManager(queue: Queue.mainQueue())
|
||||||
self.contactDataManager = DeviceContactDataManagerImpl()
|
self.contactDataManager = DeviceContactDataManagerImpl(accountManager: accountManager)
|
||||||
} else {
|
} else {
|
||||||
self.locationManager = nil
|
self.locationManager = nil
|
||||||
self.contactDataManager = nil
|
self.contactDataManager = nil
|
||||||
|
Loading…
x
Reference in New Issue
Block a user