Sign and notarize

Staple and verify the ticket

Attach the notarization ticket where supported and verify signatures, Gatekeeper assessment, and stapling on the exact artifact you will publish.

12 minute lesson

~~~

Notarization produced a ticket, but the ticket lives on Apple’s servers. Stapling attaches a copy to your artifact, so Gatekeeper can verify the app even when the user’s Mac is offline or Apple’s servers are unreachable at launch time.

Staple to the app (or to the DMG, if that’s your deliverable), then confirm the staple took:

xcrun stapler staple Notes.app
xcrun stapler validate Notes.app

staple prints a success message, and validate confirms the ticket is readable from the artifact itself.

Now verify the whole chain the way Gatekeeper will. Two checks, two different questions. First, is the signature intact on every nested component?

codesign --verify --deep --strict --verbose=2 Notes.app

Second, would macOS actually let this app run?

spctl -a -vv Notes.app

The output you want ends with accepted and source=Notarized Developer ID. If it says rejected, stop — users will see a block dialog, and you need the reason before shipping.

One clarification on --deep, because it trips people up. It’s a verification convenience that walks nested code. It is not a signing strategy. Sign nested components correctly from the inside out (Xcode’s export does this for you) and use --deep only to catch mistakes.

Run these checks twice: on the artifact before packaging, and again on an app extracted from the final download. That second run catches a classic ordering bug: you zipped the app, submitted the ZIP for notarization, stapled the app afterwards — but the ZIP you published still contains the unstapled copy. Offline Macs then get a worse first launch than the one you tested. Staple first, package second.

Lesson completed

Take this course offline

Get every free book and course as PDF and EPUB files.

Get the download library →