Hi,
I used this code for accessing the Mac address book for a while, but since MacOS Catalina (or even before) this is no longer working. Forum user Wilbert found that this is outdated and now the CNContactStore should be used.
Did anyone use this before with PureBasic and CocoaMessage()? Any code examples available?
Accessing Mac CNContactStore
Re: Accessing Mac CNContactStore
The following example is working to access the CNContactStore and list all contacts with phone numbers and/or eMail addresses. I have tested the example successfully in these MacOS versions:
It took me many many hours to program a working example in PureBasic. I programmed more than 40 not working test programs in PureBasic and even a dozen test programs in Objective C and Swift which sometimes kind of worked but also displayed several severe warnings. Because of Apple's security settings (which also became more strict with newer MacOS versions) this was a minefield and I paused several times because I didn't seem to come any further. As a last resort in my despair I tried to find help by opening a session with ChatGPT 4o mini and against all odds I was able to find a first PureBasic example which finally worked. Of course the examples proposed by ChatGPT didn't work but by asking ChatGPT to correct the errors, to use CocoaMessage() instead of importing API calls and adding other crucial code lines by myself after about 20 minutes of cooperative work I unbelievably had a first working example!
In order to work with contacts, the program has to ask the user to allow access to the AddressBook. To test the sourcecode in the PureBasic IDE, the program in the IDE also has to ask for this allowance in an automatic requester (by the OS). In Yosemite to Ventura this should work. In Sonoma and Sequoia this didn't work. After compiling the example code to an app, the started app also should display the requester for allowance to access the AddressBook. This worked relatively reliably for Yosemite to Sequioa.
For obtaining the OS access request in the IDE on El Capitan to Sierra it was possible to execute my example code in PB 6.04. In Big Sur to Ventura I had to run the example code in PB 5.73 (PB 6.04 or 6.12 don't trigger the requester). In Sonoma to Sequoia I was not able to trigger the access request in the IDE at all because PB 5.73 doesn't run anymore on Sonoma and Sequoia and PB 6.04 and 6.12 don't trigger the access requester.
When having problems with the access to the AddressBook you may take a look into the MacOS securty settings:
System settings > Privacy & Security > Contacts
(or in German:
Systemeinstellungen > Datenschutz & Sicherheit > Kontakte)
You are able to activate or deactivate each app by using a switch in the contacts list. But you are not able to delete a program entry. To delete an entry you have to open a console and use the tccutil console program:
The app name PureBASIC-6.11-x64 may be different in your case because it depends on which has been the first PureBasic version you installed on your MacOS. But all PureBasic versions work with this registered version name. They all result in the AppID com.fantaisiesoftware.purebasicide. So for the PureBasic IDE the osascript step wouldn't be necessary.
- 10.11.6 'El Capitan'
- 10.12.6 'Sierra'
- 10.13.6 'High Sierra'
- 10.14.6 'Mojave'
- 10.15.7 'Catalina'
- 11.7.10 'Big Sur'
- 12.7.6 'Monterey'
- 13.7.1 'Ventura'
- 14.7.1 'Sonoma'
- 15.1.1 'Sequoia'
It took me many many hours to program a working example in PureBasic. I programmed more than 40 not working test programs in PureBasic and even a dozen test programs in Objective C and Swift which sometimes kind of worked but also displayed several severe warnings. Because of Apple's security settings (which also became more strict with newer MacOS versions) this was a minefield and I paused several times because I didn't seem to come any further. As a last resort in my despair I tried to find help by opening a session with ChatGPT 4o mini and against all odds I was able to find a first PureBasic example which finally worked. Of course the examples proposed by ChatGPT didn't work but by asking ChatGPT to correct the errors, to use CocoaMessage() instead of importing API calls and adding other crucial code lines by myself after about 20 minutes of cooperative work I unbelievably had a first working example!
In order to work with contacts, the program has to ask the user to allow access to the AddressBook. To test the sourcecode in the PureBasic IDE, the program in the IDE also has to ask for this allowance in an automatic requester (by the OS). In Yosemite to Ventura this should work. In Sonoma and Sequoia this didn't work. After compiling the example code to an app, the started app also should display the requester for allowance to access the AddressBook. This worked relatively reliably for Yosemite to Sequioa.
For obtaining the OS access request in the IDE on El Capitan to Sierra it was possible to execute my example code in PB 6.04. In Big Sur to Ventura I had to run the example code in PB 5.73 (PB 6.04 or 6.12 don't trigger the requester). In Sonoma to Sequoia I was not able to trigger the access request in the IDE at all because PB 5.73 doesn't run anymore on Sonoma and Sequoia and PB 6.04 and 6.12 don't trigger the access requester.
When having problems with the access to the AddressBook you may take a look into the MacOS securty settings:
System settings > Privacy & Security > Contacts
(or in German:
Systemeinstellungen > Datenschutz & Sicherheit > Kontakte)
You are able to activate or deactivate each app by using a switch in the contacts list. But you are not able to delete a program entry. To delete an entry you have to open a console and use the tccutil console program:
Code: Select all
- Remove all apps for the category Contacts:
> tccutil reset AddressBook
- Remove single app (for example PureBasic IDE) from Contacts:
- Get Bundle-ID of PureBasic IDE (will return com.fantaisiesoftware.purebasicide):
> osascript -e 'id of app"PureBASIC-6.11-x64"'
- Remove PureBasic IDE from Contacts:
> tccutil reset AddressBook com.fantaisiesoftware.purebasicide
Code: Select all
EnableExplicit
#KeyList = "givenName+familyName+phoneNumbers+emailAddresses"
ImportC "-framework Contacts"
EndImport
Define i.I
Define Info.S
Define Result.S
NewList Key.S()
Procedure.S GetContactsFromAddressBook(KeyList.S)
Protected ClassName.S
Protected Contact.I
Protected ContactArray.I
Protected ContactCount.I
Protected ContactInfo.S
Protected ContactStore.I
Protected Error.S
Protected i.I
Protected Item.I
Protected ItemCount.I
Protected ItemArray.I
Protected j.I
Protected k.I
Protected Key.S
Protected KeyArray.I
Protected KeyCount.I
ContactStore = CocoaMessage(0, 0, "CNContactStore new")
If ContactStore = 0
ProcedureReturn "ERROR: Failed to create CNContactStore instance!"
EndIf
KeyCount = CountString(KeyList, "+") + 1
; ----- Create mutable array with 1st key name
Key = StringField(KeyList, 1, "+")
KeyArray = CocoaMessage(0, 0, "NSMutableArray arrayWithObject:$", @Key)
If KeyCount > 1
; ----- Add keys 2 to n to mutable array
For i = 2 To KeyCount
Key = StringField(KeyList, i, "+")
CocoaMessage(0, KeyArray, "addObject:$", @Key)
Next i
EndIf
; ----- Fetch all unified contacts containing the keys in key array
ContactArray = CocoaMessage(0, ContactStore,
"unifiedContactsMatchingPredicate:", 0,
"keysToFetch:", KeyArray,
"error:", @Error)
If ContactArray = 0
CocoaMessage(0, ContactStore, "release")
ProcedureReturn "ERROR: Fetching of the contacts failed!"
EndIf
ContactCount = CocoaMessage(0, ContactArray, "count")
ContactInfo = "Contacts found: " + ContactCount + #CR$ + #CR$
If ContactCount > 0
For k = 1 To ContactCount
Contact = CocoaMessage(0, ContactArray, "objectAtIndex:", k - 1)
ContactInfo + "Contact " + Str(k) + #CR$
For j = 1 To KeyCount
Key = StringField(KeyList, j, "+")
Item = CocoaMessage(0, Contact, Key)
ClassName = PeekS(CocoaMessage(0, CocoaMessage(0, Item, "className"),
"UTF8String"), -1, #PB_UTF8)
; ----- Check whether the returned item is an array
If FindString(ClassName, "Array") = 0
ContactInfo + Key + ": " + PeekS(CocoaMessage(0, CocoaMessage(0,
Contact, Key), "UTF8String"), -1, #PB_UTF8) + #CR$
Else
ItemArray = Item
ItemCount = CocoaMessage(0, ItemArray, "count")
ContactInfo + Key + ":" + #CR$
For i = 1 To ItemCount
Item = CocoaMessage(0, ItemArray, "objectAtIndex:", i - 1)
If FindString("phoneNumbers", Key)
ContactInfo + "- " + PeekS(CocoaMessage(0, CocoaMessage(0,
CocoaMessage(0, Item, "value"), "stringValue"), "UTF8String"),
-1, #PB_UTF8) + #CR$
ElseIf FindString("emailAddresses", Key)
ContactInfo + "- " + PeekS(CocoaMessage(0, CocoaMessage(0,
Item, "value"), "UTF8String"), -1, #PB_UTF8) + #CR$
Else
CocoaMessage(0, ContactStore, "release")
ProcedureReturn "ERROR: Array evaluation for key " + Key +
"is currently not implemented!"
EndIf
Next i
EndIf
Next j
ContactInfo + #CR$
Next k
EndIf
; ----- Release the CNContactStore object
CocoaMessage(0, contactStore, "release")
ProcedureReturn ContactInfo
EndProcedure
Result = GetContactsFromAddressBook(#KeyList)
If Left(Result, 5) = "ERROR"
MessageRequester("ERROR", Mid(Result, 7))
End
EndIf
If Result = ""
Info = "No Contacts were found!"
Else
Info = Result
EndIf
OpenWindow(0, 0, 0, 270, 300, "Address book contacts",
#PB_Window_SystemMenu | #PB_Window_ScreenCentered)
EditorGadget(0, 5, 5, WindowWidth(0) - 10, WindowHeight(0) - 10)
AddGadgetItem(0, 0, Info)
Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow
Re: Accessing Mac CNContactStore
Great, thanks for sharing! 

Re: Accessing Mac CNContactStore
Now I have also found a solution to obtain the access requester for contacts in the IDE of Sonoma and Sequoia. Load my example from above in PB 5.73 and add the following line above ImportC:Shardik wrote: Sat Nov 23, 2024 6:32 pm For obtaining the OS access request in the IDE on El Capitan to Sierra it was possible to execute my example code in PB 6.04. In Big Sur to Ventura I had to run the example code in PB 5.73 (PB 6.04 or 6.12 don't trigger the requester). In Sonoma to Sequoia I was not able to trigger the access request in the IDE at all because PB 5.73 doesn't run anymore on Sonoma and Sequoia and PB 6.04 and 6.12 don't trigger the access requester.
Code: Select all
Import "-fno-pie" : EndImport
Unfortunately this solution still doesn't work in PB 6.12 and 6.20 Beta 3 (Intel processor) either with Asm or C backend. The access requester is only triggered when running my example with PB 5.73.
Here you find a detailed analysis why the Asm backend with Intel processor doesn't work in PB 5.73, 6.04 and 6.12 up to 6.20 Beta 2. In PB 6.20 Beta 3 Fred implemented my Import line, so that the Asm backend is working again without the above Import line.