I am developing an app that would like to access the contacts on the user's device. There was a recent article published that said Apple was restricting access to a user's contacts on their device. This was due to some current apps accessing the contacts and using that list for spam email and texts.
Is that they case? if so, are there specific cases that allow an App to access the contacts?
Contacts
RSS for tagAccess the user's contacts and format and localize contact information using Contacts.
Posts under Contacts tag
46 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hello,
I’m experiencing an issue with Siri on iOS where it prioritizes a contact from the wrong account, even though I’ve set a default account for Contacts.
Details of the issue:
I have two contact groups:
Exchange (Outlook) — my default account.
iCloud.
There’s a contact, "Alena Jorse," which exists in both groups:
In the Exchange group, the name is saved as Alena Jorse.
In the iCloud group, it is saved as Alena Jorse with double-strike formatting (e.g., "Alena Jorse**").
Both contacts have the same phone number.
When I ask Siri to call "Alena Jorse," it selects the iCloud contact ("Alena Jorse**") instead of the Exchange contact, despite Exchange being set as the default Contacts account in my iOS settings.
Expected Behavior:
Siri should prioritize the contact from the default account (Exchange) and ignore other accounts unless specified.
Steps to Reproduce:
Have duplicate contacts in two groups (Exchange and iCloud) with the same phone number.
Set Exchange as the default Contacts account.
Ask Siri to call the contact.
Troubleshooting Steps Taken:
Ensured the default account is set to Exchange.
Verified both contacts have the same phone number.
Tested by disabling iCloud temporarily, which resolves the issue (but is not a viable long-term solution).
Request:
Could you please advise if this is intended behavior or a bug? If it’s a configuration issue, how can I ensure Siri prioritizes the default account for contacts? If it’s a bug, could this be investigated further?
Thank you for your assistance.
I wanted to create a public contact card so when my clients click a button on my website it traverses them through the iMessage API and instantly adds a contact card with photo and name as a background task. If there is implications my next question was going to be if there is a possible way to create a public contact card where for example I could redirect a client to iMessage and when starting a new conversation if they type in a name it pops up instead of needing to utilize a phone number? I am trying to explain my thought process as much as possible but I have been typing for hours. In simple terms is there a possibility to create a public contact card that can be accessed or texted by anyone who types in a name into new conversation bar?
Hi,
In my app, I have an option to remove a contact from a contact group (using the Contacts framework), and it's been working fine till recently users of the macOS version reported that it's not working. I have been using the CNSaveRequest removeMember(contact, from: group) API. The same API works fine on iOS. I'm not sure when it started but it seems to be affecting macOS14.6 as well as 15.1.
I was able to reproduce it in a small test project as well, and have the same experience (the API works on iOS but not on macOS), so it definitely seems like a problem with the framework. Can someone confirm this, and/or suggest a workaround?
Here's the code I run to test it out ...a simple SwiftUI view that has 4 buttons:
Create contact and group
Add contact to group
Remove contact from group
(optional) cleanup by deleting contact and group
It's the 3rd step that seems to fail on macOS, but works fine on iOS.
Here's the code to test it out:
struct ContentView: View {
let contactsModel = ContactsStoreModel()
var body: some View {
VStack (alignment: .center, spacing: 15){
Button ("1. Add Contact And Group") {
print("add contact button pressed")
contactsModel.addTestContact()
if let _ = contactsModel.createdContact {
print("created contact success")
}
}
Button ("2. Add Contact To Group") {
print("add to group button pressed")
contactsModel.addContactToGroup()
}
Button ("3. Remove Contact From Group") {
print("remove from group button pressed")
contactsModel.removeContactFromGroup()
}
Button ("4. Delete Contact and Group") {
print("remove from group button pressed")
contactsModel.deleteContactAndGroup()
}
}
.padding()
}
}
#Preview {
ContentView()
}
@available(iOS 13.0, *)
@objc final class ContactsStoreModel: NSObject, ObservableObject {
let contactStore = CNContactStore()
var createdContact : CNContact?
var createdGroup : CNGroup?
public func addTestContact() {
let storeContainer = contactStore.defaultContainerIdentifier()
let contact = CNMutableContact()
contact.givenName = "Testing"
contact.familyName = "User"
contact.phoneNumbers = [CNLabeledValue(label: "Cell", value: CNPhoneNumber(stringValue: "1234567890"))]
let group = CNMutableGroup()
group.name = "Testing Group"
print("create contact id = \(contact.identifier)")
print("create group id = \(group.identifier)")
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.add(contact, toContainerWithIdentifier: storeContainer)
saveRequest.add(group, toContainerWithIdentifier: storeContainer)
try contactStore.execute(saveRequest)
createdContact = contact
createdGroup = group
} catch {
print("error in store execute = \(error)")
}
}
public func addContactToGroup() {
if let contact = createdContact, let group = createdGroup {
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.addMember(contact, to: group)
try contactStore.execute(saveRequest)
}
catch {
print("error in store execute = \(error)")
}
}
}
public func removeContactFromGroup() {
if let contact = createdContact, let group = createdGroup {
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.removeMember(contact, from: group)
try contactStore.execute(saveRequest)
}
catch {
print("error in store execute = \(error)")
}
}
}
public func addGroupAndContact() {
let storeContainer = contactStore.defaultContainerIdentifier()
let group = CNMutableGroup()
group.name = "Test Group"
print("create group id = \(group.identifier)")
if let contact = createdContact {
do {
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.add(group, toContainerWithIdentifier: storeContainer)
saveRequest.addMember(contact, to: group)
try contactStore.execute(saveRequest)
createdGroup = group
} catch {
print("error in store execute = \(error)")
}
}
}
public func deleteContactAndGroup() {
if let contact = createdContact, let group = createdGroup {
do {
let mutableGroup = group.mutableCopy() as! CNMutableGroup
let mutableContact = contact.mutableCopy() as! CNMutableContact
let saveRequest = CNSaveRequest()
saveRequest.transactionAuthor = "TestApp"
saveRequest.delete(mutableContact)
saveRequest.delete(mutableGroup)
try contactStore.execute(saveRequest)
}
catch {
print("error in deleting store execute = \(error)")
}
}
}
}
For years every time I click on the phone app in my car it use to show resent calls now it opens favourites by default. how can I change it?
I am considering an implementation to provide OS Contacts information in real-time using data from push notifications.
Through a TSI (Technical Support Incident), Apple Support recommended combining ContactProviderExtension and NotificationServiceExtension. However, the following threads indicate that this combination is not supported, as confirmed by Apple DTS (Thread 1, Thread 2).
Question 1: Latest Support Status for Using ContactProviderExtension with NotificationServiceExtension
Additionally, there is limited documentation on implementing ContactProviderExtension, and I am specifically struggling with the following points:
Question 2: Specific Implementation Guidance for ContactProviderExtension
The method to call when providing Contacts information to the OS
How to add and delete Contacts information provided to the OS
How to verify the Contacts information currently provided to the OS
Any insights on the latest support status or specific implementation methods for these extensions would be greatly appreciated.
I have an app that needs to refresh a server whenever a Contacts record is updated. I can observe Contacts, but that only seems to work when my app is running (and in foreground, which it cannot be on iPhone if the Contacts app is being updated). I want it to process, even if my app is in background, or has been terminated (swiped away), or after a phone restart.
The only way I can think of is to periodically push a notification to the app from an external server. Is there any way to run a timer that sends a notification to the app on a periodic basis? The timers you can set seem to run even if the Clock app is swiped away, or following a phone restart. Is there anything like that I could use to wake my app periodically?
Hello,
I am a developer currently working on a personal contact management app.
What is the app?
My app stores additional information beyond basic contact details. Therefore, instead of using the Contacts framework, I manage contact objects using Core Data.
What am I trying to achieve?
I want to display additional information on the caller ID screen when a call is received from a number stored in my app.
What have I tried?
I’ve attempted the following methods without success:
1. Call Directory Extension:
I thought using this method would allow me to display additional information from Core Data on the call screen. However, I learned that when a call is received, the iOS system first searches for the phone number in the Contacts app and only looks to the Extension app if no match is found. Therefore, displaying contact information from my app seems unfeasible.
2. Custom Call UI:
Using CallKit seemed like a viable option to display the necessary information during a call, but it appears to only be possible with VoIP apps. My app does not support VoIP calls, so this method was also not implementable.
I am wondering if there are any technologies available that could help me achieve my goal, or if there’s something I might be missing. Any advice would be greatly appreciated.
Thank you!
If a similar question has been asked, I apologize for the repetition.
I have an application that updates bulk contacts it used to take 2 seconds to update 100 contacts on < IOS 18. After IOS 18 it takes almost 2 minutes and is highly affecting our app performance.
I backed up my data in iCloud.com and tried to use the past contact archives but it didnt work. Also tried adding them one by one but didn’t save into my contact list. Please help
I have an app that was written in UIKit. It's too large, and it would be much too time consuming at this point to convert it to SwiftUI.
I want to incorporate the new limited contacts into this app. The way it's currently written everything works fine except for showing the limited contacts in the contact picker.
I have downloaded and gone though the Apple tutorial app but I'm having trouble thinking it through into UIKit. After a couple of hours I decided I need help.
I understand I need to pull the contact IDs of the contacts that are in the limited contacts list. Not sure how to do that or how to get it to display in the picker. Any help would be greatly appreciated.
func requestAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void)
{
switch CNContactStore.authorizationStatus(for: .contacts)
{
case .authorized:
completionHandler(true)
case .denied:
showSettingsAlert(completionHandler)
case .restricted, .notDetermined:
CNContactStore().requestAccess(for: .contacts) { granted, error in
if granted
{
completionHandler(true)
} else {
DispatchQueue.main.async { [weak self] in
self?.showSettingsAlert(completionHandler)
}
}
}
// iOS 18 only
case .limited:
completionHandler(true)
@unknown default: break
}
}
// A text field that displays the name of the chosen contact
@IBAction func contact_Fld_Tapped(_ sender: TextField_Designable)
{
sender.resignFirstResponder()
// The contact ID that is saved to the Db
getTheCurrentContactID()
let theAlert = UIAlertController(title: K.Titles.chooseAContact, message: nil, preferredStyle: .actionSheet)
// Create a new contact
let addContact = UIAlertAction(title: K.Titles.newContact, style: .default) { [weak self] _ in
self?.requestAccess { _ in
let openContact = CNContact()
let vc = CNContactViewController(forNewContact: openContact)
vc.delegate = self // this delegate CNContactViewControllerDelegate
DispatchQueue.main.async {
self?.present(UINavigationController(rootViewController: vc), animated: true)
}
}
}
let getContact = UIAlertAction(title: K.Titles.fromContacts, style: .default) { [weak self] _ in
self?.requestAccess { _ in
self?.contactPicker.delegate = self
DispatchQueue.main.async {
self?.present(self!.contactPicker, animated: true)
}
}
}
let editBtn = UIAlertAction(title: K.Titles.editContact, style: .default) { [weak self] _ in
self?.requestAccess { _ in
let store = CNContactStore()
var vc = CNContactViewController()
do {
let descriptor = CNContactViewController.descriptorForRequiredKeys()
let editContact = try store.unifiedContact(withIdentifier: self!.oldContactID, keysToFetch: [descriptor])
vc = CNContactViewController(for: editContact)
} catch {
print("Getting contact to edit failed: \(self!.VC_String) \(error)")
}
vc.delegate = self // delegate for CNContactViewControllerDelegate
self?.navigationController?.isNavigationBarHidden = false
self?.navigationController?.navigationItem.hidesBackButton = false
self?.navigationController?.pushViewController(vc, animated: true)
}
}
let cancel = UIAlertAction(title: K.Titles.cancel, style: .cancel) { _ in }
if oldContactID.isEmpty
{
editBtn.isEnabled = false
}
theAlert.addAction(getContact) // Select from contacts
theAlert.addAction(addContact) // Create new contact
theAlert.addAction(editBtn) // Edit this contact
theAlert.addAction(cancel)
let popOver = theAlert.popoverPresentationController
popOver?.sourceView = sender
popOver?.sourceRect = sender.bounds
popOver?.permittedArrowDirections = .any
present(theAlert,animated: true)
}
func requestAccess(completionHandler: @escaping (_ accessGranted: Bool) -> Void)
{
switch CNContactStore.authorizationStatus(for: .contacts)
{
case .authorized:
completionHandler(true)
case .denied:
showSettingsAlert(completionHandler)
case .restricted, .notDetermined:
CNContactStore().requestAccess(for: .contacts) { granted, error in
if granted
{
completionHandler(true)
} else {
DispatchQueue.main.async { [weak self] in
self?.showSettingsAlert(completionHandler)
}
}
}
// iOS 18 only
case .limited:
completionHandler(true)
@unknown default: break
}
}
// MARK: - Contact Picker Delegate
extension AddEdit_Quote_VC: CNContactPickerDelegate
{
func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact)
{
selectedContactID = contact.identifier
let company: String = contact.organizationName
let companyText = company == "" ? K.Titles.noCompanyName : contact.organizationName
contactNameFld_Outlet.text = CNContactFormatter.string(from: contact, style: .fullName)!
companyFld_Outlet.text = companyText
save_Array[0] = K.AppFacing.true_App
setSaveBtn_AEQuote()
}
}
extension AddEdit_Quote_VC: CNContactViewControllerDelegate
{
func contactViewController(_ viewController: CNContactViewController, shouldPerformDefaultActionFor property: CNContactProperty) -> Bool
{
return false
}
func contactViewController(_ viewController: CNContactViewController, didCompleteWith contact: CNContact?)
{
selectedContactID = contact?.identifier ?? ""
if selectedContactID != ""
{
let company: String = contact?.organizationName ?? ""
let companyText = company == "" ? K.Titles.noCompanyName : contact!.organizationName
contactNameFld_Outlet.text = CNContactFormatter.string(from: contact!, style: .fullName)
companyFld_Outlet.text = companyText
getTheCurrentContactID()
if selectedContactID != oldContactID
{
save_Array[0] = K.AppFacing.true_App
setSaveBtn_AEQuote()
}
}
dismiss(animated: true, completion: nil)
}
}
The ContactAccessButton doesn't seem to work correctly in dark mode. The background color is black and looks buggy even in the Apple provided sample app.
When access is denied, the ContactAccessButton text isn't even visible. Light mode vs dark mode:
When using both the ContactsAccessButton demo project, as well as when implementing it in my own, the whole app freezes after entering a few characters and searching through contacts.
I don't know if this is necessarily reproducable because it's probably related to the contacts in my contact book. Typing in "Lex" does not freeze the app, but typing in "Adam No" freezes it.
I get the following console error before my app freezes and I'm forced to force quit it:
#ContactsButton Failed to get service proxy: Error Domain=NSCocoaErrorDomain Code=4097 "connection to service with pid 3387 named com.apple.ContactsUI.ContactsButtonXPCService" UserInfo={NSDebugDescription=connection to service with pid 3387 named com.apple.ContactsUI.ContactsButtonXPCService}
#ContactsButton Failed to get remote content: nil (got this a number of times
ContactsAccessButton really doesn't seem production ready...
We're adjusting to the new iOS 18 .limited contacts access mode.
In our app, we don't request contacts right away. We have a search bar where users can search through their own contacts and select one using the ContactAccessButton. If they do select one, then they're prompted to "Grant Limited Access", not "Grant Full Access", as the screenshot shows below.
Later on, we want to offer the ability for users to sync their entire contact book with our app. This will improve their experience on the app by automatically finding all their friends already on the app, without them having to do the manual work of clicking on every single contact in the ContactsAccessPicker.
Is this possible right now?
It doesn't seem like it—when I call ContactsStore.requestAccess(for: .contacts) while in .limited access mode, nothing happens. But I would like to show a prompt that gives the user the ability to grant all their contacts to improve their experience.
ERROR ITMS-90349: "Invalid Info.plist value. The value of the EXExtensionPointldentifier key, com.apple.contact.provider.extension, in the Info.plist of "MainApp.app/Extensions/ContactProviderExtension.appex" is invalid.
We were working on new iOS18 Contacts Provider extension and when try to test the feature in testflight we were unable to submit the build and getting the above error. The extensionPointldentifier 'com.apple.contact.provider.extension' was auto generated by xcode and apple doc mentioned the same value to use for Contacts Provider extension support. But it is not accepting in testflight.
https://developer.apple.com/documentation/contactprovider/contactproviderextension
Any help will be appreciated.
I updated to the newest version of iOS 18 Beta this morning. One feature I preferred on the previous version was that when you're going into the call screen, you had to press the small phone icon rather than the persons name, to make the call. It stopped me accidentally calling people a lot of the time.. This feature seems to have gone on the newest version, or is that a bug?
Essentially, the little (i) icon become a phone icon, and you used that to call, rather than the main contact name box.
We are trying to adopt the new iOS18 ContactAccessButton and ContactAccessPicker in our app and we are facing below issues and requirements in UI and functionality.
is there a way to optionally hide the ContactAccessButton UI when there is no matching results ?.
The search button in the ContactAccessButton UI is not working when showing no matches and browse contacts. The console showing below error
#ContactsButton response after touch -- Should not show UI #ContactsButton: match for callback was unexpectedly nil?
The ContactAccessPicker view and contact selection view opened from ContactAccessButton - view results are not presented properly. The presented view not fully covered the presenting screen and blank screen appears in edges
Cancel button in ContactAccessPicker UI when list shown is not working.
I'm facing an issue with the enumerateContent method in my Contact Provider extension. The method is being called multiple times before I call the first observer.didEnumerate..
Here's a sample of my code:
class TestContactProviderRootContainerEnumerator : ContactItemEnumerator {
let handler = TestContactProviderUsecaseHandler()
func configure(for domain: ContactProviderDomain) {
log.error("---> configure")
}
func enumerateContent(in page: ContactItemPage, for observer: ContactItemContentObserver) {
let requestPage = getPageIndex(from: page.offset, pageSize: observer.suggestedPageSize)
log.error("---> Begin Enumerate Content page=\(requestPage) pageSize =\(observer.suggestedPageSize)")
func completion(items: [ContactItem], hasMore: Bool) {
observer.didEnumerate(items)
do {
let generationMarker = try getGenerationMarker(page: page)
if hasMore {
let nextPage = ContactItemPage(generationMarker: generationMarker, offset: page.offset + items.count)
log.error("---> nextPage set offset \(page.offset + items.count) nextPage: \(getPageIndex(from: page.offset + items.count, pageSize: observer.suggestedPageSize))")
observer.didFinishEnumeratingPage(upTo: nextPage)
}else {
observer.didFinishEnumeratingContent(upTo: generationMarker)
}
}catch {
observer.didFinishEnumeratingContentWithError(error)
return
}
}
log.error("---> Start Request page=\(requestPage) pageSize =\(observer.suggestedPageSize)")
handler.requestForRecordsList(page: requestPage, perPage: observer.suggestedPageSize, sortBy: Field.Contact.lastName, modifiedSince: nil, completion: { contactItems, hasMore, error in
log.error("---> Finish Request page=\(requestPage) pageSize =\(observer.suggestedPageSize)")
if let error {
log.error("---> Error: \(error)")
observer.didFinishEnumeratingContentWithError(error)
return
}
completion(items: contactItems, hasMore: hasMore)
log.error("---> End Enumerate Content page=\(requestPage) pageSize =\(observer.suggestedPageSize)")
})
}
}
Below are the logs that I'm seeing:
---> configure
---> Begin Enumerate Content page=1 pageSize =20
---> Start Request page=1 pageSize =20
---> Begin Enumerate Content page=1 pageSize =20
---> Begin Enumerate Content page=1 pageSize =20
---> Begin Enumerate Content page=1 pageSize =20
....10+times
---> Begin Enumerate Content page=1 pageSize =20
---> Finish Request page=1 pageSize =20
---> Begin Enumerate Content page=1 pageSize =20
---> nextPage set offset 20 nextPage: 2
---> Begin Enumerate Content page=1 pageSize =20
---> Begin Enumerate Content page=1 pageSize =20
---> Begin Enumerate Content page=2 pageSize =20
---> Begin Enumerate Content page=2 pageSize =20
---> Start Request page=2 pageSize =20
---> Begin Enumerate Content page=2 pageSize =20
---> Begin Enumerate Content page=2 pageSize =20
....10+times
---> Begin Enumerate Content page=2 pageSize =20
---> Begin Enumerate Content page=2 pageSize =20
Has anyone else experienced this issue? Any ideas on why this is happening or how to fix it.
A Notification Service Extension is one of the more capable extensions, and there's a lot that can be done within it (for example, it can invoke a Call Extension).
However its not possible to use a Contact Provider Extension within it.
If a CPE has been enabled by the main app, then if a push is sent to the NSE, then within the NSE the ContactProviderManager class reports that the CPE is disabled and its not possible to anything with it. For example a call to ContactProviderManager.signalEnumerator() will hang and not complete.
I was hoping to create a contact and make it available to the system on receipt of a push, but this isn't going to possible.
Is this intentional and by design, or just due to the immaturity of this feature/iOS beta?
The documentation of a Contact provider Extension says:
"signalEnumerator() An example of using this call is to handle a push notification to your app when the provided contacts from your server update"
It therefore seems strange that the main cited use case for ContactProviderManager.signalEnumerator() isn't actually possible if the push is delivered to an extension rather than the app.
In the documentation for the Contact Provider Extension
contact provider it says
Use the Contact Provider framework if your app manages its own contacts and wants to make them available in other apps that use the Contacts framework.
But how does an app manage its own contacts? What needs to be done differently if it manages its own contacts versus managing a user's contacts? Does the user still need to grant contacts access for example?
Is there a special group/domain that should be used (how) to add app contacts?
There's no mention of any of this that I can see in the documentation for CNContact or CNContactStore.