How to codesign CLI tool so that I can read CNContact.note field?

I want to build a CLI tool (using SwiftPM - without XCode) to read the contacts on my mac. The end goal is to use the notes field or maybe custom fields to build a simple CRM (customer relationship tool) to keep track of some things. It especially means reading the NOTE field, and also writing it back.

But... as mentioned on com.apple.developer.contacts.notes | Apple Developer Documentation reading the note field requires the com.apple.developer.contacts.notes.

How do I do that? If it runs locally only on my machine I am happy.

I wrote an entitlements.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.developer.contacts.notes</key>
    <true/>
</dict>
</plist>

And do

# build
swift build --configuration release --disable-sandbox --arch arm64
Building for production...
[2/2] Linking contacts
Build complete! (0.29s)

#sign
codesign --sign - --entitlements entitlements.plist --deep .build/release/contacts --force
.build/release/contacts: replacing existing signature

But upon running, I get:

./.build/release/contacts
fish: Job 1, './.build/release/contacts' terminated by signal SIGKILL (Forced quit)

Without signing I get:

*** Terminating app due to uncaught exception 'CNPropertyNotFetchedException', reason: 'A property was not requested when contact was fetched.'
*** First throw call stack:
(
        0   CoreFoundation                      0x000000018b1cc570 __exceptionPreprocess + 176
        1   libobjc.A.dylib                     0x000000018acbdeb4 objc_exception_throw + 60
        2   CoreFoundation                      0x000000018b1cc460 +[NSException exceptionWithName:reason:userInfo:] + 0
        3   Contacts                            0x000000019f8f9b74 -[CNContact note] + 152
        4   contacts                            0x0000000104879e04 $s8contacts3CliV3runyyKF + 436
        5   contacts                            0x000000010487a0c8 $s8contacts3CliV14ArgumentParser15ParsableCommandAadEP3runyyKFTW + 12
        6   contacts                            0x000000010487a160 contacts_main + 96
        7   dyld                                0x000000018acf90e0 start + 2360
)
libc++abi: terminating due to uncaught exception of type NSException
fish: Job 1, './.build/release/contacts' terminated by signal SIGABRT (Abort)

I am new to Swift and SwiftPM and the world of code signing.

I currently am NOT a member of the Apple Developer program but if needed I am (reluctantly) willing to pay 99$ to be able to sign/notarize/.. but since the goal is to only run it for myself I hppe there is a way to self-sign.

Do I need so sign my cli? Is it even possible to codesign command line tools (i've seen comments that it is not)? How would I do that? What am I missing?

Cheers, Oliver

Replies

There’s two issues here:

  • Accessing the notes field.

  • Doing that from a command-line tool.

Accessing the notes field requires a managed capability. To quote that web page:

Before you submit an app with this entitlement to the App Store, you need to get permission to use the entitlement. Request permission at …

Have you done that? Was your request approved?


Oh wait, you wrote:

I currently am NOT a member of the Apple Developer program

which kinda answers my question. You need to be a member of a paid developer program to work apply for a managed capability.


On the command-line tool front, com.apple.developer.contacts.notes is a restricted entitlement, one that must be authorised by a provisioning profile. Using restricted entitlements from a command-line tool is tricky, because there’s no place to place your provisioning profile so that the trusted execution system can find it. I generally recommend the workaround described in Signing a daemon with a restricted entitlement.


It sounds like you’re building a solution for yourself, not a product that you plan to ship to a wide range of users. In that case I suspect that this path is going to be a dead end, at least as far as the notes field is concerned.

A better avenue to explore is AppleScript. Contacts is scriptable and scripts can access the notes field. For example:

tell application "Contacts"
	note of my card
end tell

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

You need to be a member of a paid developer program to work apply for a managed capability.

I am blocked on that front. Something went wrong with enrollment and I'm currently waiting on Apple support. I'm guessing it's because I'm living in a different country than my nationality.

But let me clarify: Even if I want to run this tool only on my own machine (for now), I have to enroll into the program, correct There is no way to run self-signed CLI tools?

On the command-line tool front, com.apple.developer.contacts.notes is a restricted entitlement, one that must be authorised by a provisioning profile. Using restricted entitlements from a command-line tool is tricky, [..] I generally recommend the workaround described in Signing a daemon with a restricted entitlement

A daemon application does not really fit the model I'm thinking of. It sounds like it's a service that is constantly running. If I understand correctly I would need to package a daemon service into an app, and then also provide a CLI tool to interact with that daemon.

Is there a way to integrate the entitlements into binary? I don't mind slightly more complicated if I can avoid the complex setup of a daemon within in an app. Is there documentation on how to do that?

It sounds like you’re building a solution for yourself, not a product that you plan to ship to a wide range of users.

True. But the hope is to be able to build something that other people might be able to use.

In conclusion: Are you saying there is no way to have single binary CLI tool build with Swift that can access the notes field?