# Signing and notarizing distributions for macOS Apple [requires](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution) all 3rd party apps to be signed and notarized (checked by Apple) for running on recent versions of macOS. ## What is covered In this tutorial, we'll show you how to sign and notarize native distributions of Compose apps (in `dmg` or `pkg` formats) for distribution on macOS. ## Prerequisites * [Xcode](https://developer.apple.com/xcode/). The tutorial was checked with Xcode 12.3. * JDK 15+. The tutorial was checked with OpenJDK 15.0.1. ## Preparing a Developer ID certificate You will need a Developer ID certificate for signing your app. #### Checking existing Developer ID certificates Open https://developer.apple.com/account/resources/certificates #### Creating a new Developer ID certificate 1. [Create a certificate signing request](https://help.apple.com/developer-account/#/devbfa00fef7): * Open `Keychain Access`. * Open the menu dialog ``` Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority ``` * Enter your Developer ID email and common name. * Check `Save to disk` option. 2. Create and install a new certificate using your [Apple Developer account](https://developer.apple.com/account/): * Open https://developer.apple.com/account/resources/certificates/add * For publishing outside the App Store, choose the `Developer ID Application` certificate type. For publishing on the App Store, you need two certificates. First select the `Mac App Distribution` certificate type, and once you have completed the steps in this section, repeat them again for the `Mac Installer Distribution` certificate type. * Upload your Certificate Signing Request from the previous step. * Download and install the certificate (drag & drop the certificate into the `Keychain Access` application). #### Viewing installed certificates You can find all installed certificates and their keychains by running the following command: ``` /usr/bin/security find-certificate -c "Developer ID Application" ``` or the following commands when publishing on the App Store: ``` /usr/bin/security find-certificate -c "3rd Party Mac Developer Application" /usr/bin/security find-certificate -c "3rd Party Mac Developer Installer" ``` If you have multiple developer certificates of the same type installed, you will need to specify the path to the keychain, containing the certificate intended for signing. ## Preparing an App ID An App ID represents one or more applications in Apple's ecosystem. #### Viewing existing App IDs Open [the page](https://developer.apple.com/account/resources/identifiers/list) on Apple's developer portal. #### Creating a new App ID 1. Open [the page](https://developer.apple.com/account/resources/identifiers/add/bundleId) on Apple's developer portal. 2. Choose `App ID` option. 3. Choose `App` type. 4. Fill the `Bundle ID` field. * A [bundle ID](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier) uniquely identifies an application in Apple's ecosystem. * You can use an explicit bundle ID a wildcard, matching multiple bundle IDs. * It is recommended to use the reverse DNS notation (e.g.`com.yoursitename.yourappname`). ## Preparing a Provisioning Profile For testing on TestFlight (when publishing to the App Store), you need to add a provisioning profile. You can skip this step otherwise. #### Checking existing provisioning profiles Open https://developer.apple.com/account/resources/profiles/list #### Creating a new provisioning profile 1. Open [the page](https://developer.apple.com/account/resources/profiles/add) on Apple's developer portal. 2. Choose `Mac App Store` option under `Distribution`. 3. Select Profile Type `Mac`. 4. Select the App ID which you created earlier. 5. Select the Mac App Distribution certificate you created earlier. 6. Enter a name. 7. Click generate and download the provisioning profile. ## Creating an app-specific password To be able to upload an app for notarization, you will need an app-specific password associated with your Apple ID. Follow these steps to generate a new password: 1. Sign in to your [Apple ID](https://appleid.apple.com/account/home) account page. 2. In the Security section, click Generate Password below App-Specific Passwords. See [this Apple support page](https://support.apple.com/en-us/HT204397) for more information on the app-specific passwords. ## Adding an app-specific password to a keychain To avoid remembering your one-time password, or writing it in scripts, you can add it to the keychain by running: ``` # Any name can be used instead of NOTARIZATION_PASSWORD xcrun altool --store-password-in-keychain-item "NOTARIZATION_PASSWORD" --username --password ``` Then you'll be able to refer to the password like `@keychain:NOTARIZATION_PASSWORD` without the need to write the password itself. ## Configuring Gradle ### Gradle DSL DSL properties should be specified in `macOS` DSL block of Compose Desktop DSL: ``` kotlin import org.jetbrains.compose.compose import org.jetbrains.compose.desktop.application.dsl.TargetFormat plugins { kotlin("jvm") id("org.jetbrains.compose") } dependencies { implementation(compose.desktop.currentOS) } compose.desktop { application { mainClass = "example.MainKt" nativeDistributions { targetFormats(TargetFormat.Dmg) macOS { // macOS DSL settings } } } } ``` ### Gradle properties Some properties can also be specified using [Gradle properties](https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties). * Default Gradle properties (`compose.desktop.mac.*`) have lower priority, than DSL properties. * Gradle properties can be specified (the items are listed in order of ascending priority): * In `gradle.properties` file in Gradle home; * In `gradle.properties` file in project's root; * In command-line ``` ./gradlew packageDmg -Pcompose.desktop.mac.sign=true ``` * Note, that `local.properties` is not a standard Gradle file, so it is not supported by default. You can load custom properties from it manually in a script, if you want. ### Configuring bundle ID ``` kotlin macOS { bundleID = "com.example-company.example-app" } ``` A [bundle ID](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier) uniquely identifies an application in Apple's ecosystem. * A bundle ID must be specified using the `bundleID` DSL property. * Use only alphanumeric characters (`A-Z`, `a-z`, and `0-9`), hyphen (`-`) and period (`.`) characters. * Use the reverse DNS notation of your domain (e.g. `com.yoursitename.yourappname`). * The specified bundle ID must match one of your App IDs. ### Configuring signing settings ``` kotlin macOS { signing { sign.set(true) identity.set("John Doe") // keychain.set("/path/to/keychain") } } ``` * Set the `sign` DSL property or to `true`. * Alternatively, the `compose.desktop.mac.sign` Gradle property can be used. * Set the `identity` DSL property to the certificate's name, e.g. `"John Doe"`. * Alternatively, the `compose.desktop.mac.signing.identity` Gradle property can be used. * Optionally, set the `keychain` DSL property to the path to the specific keychain, containing your certificate. * Alternatively, the `compose.desktop.mac.signing.keychain` Gradle property can be used. * This step is only necessary, if multiple developer certificates of the same type are installed. The following Gradle properties can be used instead of DSL properties: * `compose.desktop.mac.sign` enables or disables signing. Possible values: `true` or `false`. * `compose.desktop.mac.signing.identity` overrides the `identity` DSL property. * `compose.desktop.mac.signing.keychain` overrides the `keychain` DSL property. Those properties could be stored in `$HOME/.gradle/gradle.properties` to use across multiple applications. ### Configuring notarization settings ``` kotlin macOS { notarization { appleID.set("john.doe@example.com") password.set("@keychain:NOTARIZATION_PASSWORD") // optional ascProvider.set("") } } ``` * Set `appleID` to your Apple ID. * Alternatively, the `compose.desktop.mac.notarization.appleID` Gradle property can be used. * Set `password` to the app-specific password created previously. * Alternatively, the `compose.desktop.mac.notarization.password` Gradle property can be used. * Don't write raw password directly into a build script. * If the password was added to the keychain, as described previously, it can be referenced as ``` @keychain:NOTARIZATION_PASSWORD ``` * Set `ascProvider` to your Team ID, if your account is associated with multiple teams. * Alternatively, the `compose.desktop.mac.notarization.ascProvider` Gradle property can be used. * To get a table of team IDs associated with a given username and password, run: ``` xcrun altool --list-providers -u -p " ``` ### Configuring provisioning profile For testing on TestFlight (when publishing to the App Store), you need to add a provisioning profile. You can skip this step otherwise. Note that this option requires JDK 18 due to [this issue](https://bugs.openjdk.java.net/browse/JDK-8274346). ``` kotlin macOS { provisioningProfile.set(project.file("embedded.provisionprofile")) } ``` Make sure to rename your provisioning profile you created earlier to `embedded.provisionprofile`. ## Using Gradle The following tasks are available: * Use `createDistributable` or `packageDmg` to get a signed application (no separate step is required). * Use `notarize` (e.g. `notarizeDmg`) to upload an application for notarization. Once the upload finishes, a `RequestUUID` will be printed. The notarization process takes some time. Once the notarization process finishes, an email will be sent to you. Uploaded file is saved to `/compose/notarization/main/-` * Use `checkNotarizationStatus` to check a status of last notarization requests. You can also use a command-line command to check any notarization request: ``` xcrun altool --notarization-info --username --password "@keychain:NOTARIZATION_PASSWORD" ```