Publish a Mac Catalyst app for ad-hoc distribution
When distributing Mac Catalyst apps outside the Mac App Store, you can also choose to distribute your app to a limited number of users on registered devices. This is known as ad-hoc distribution, and is primarily used for testing apps within a group of people. However, it's limited to 100 devices per membership year, and the devices must be added to your Apple Developer Account. Members of the Apple Developer Program and the Apple Developer Enterprise Program can use this distribution approach.
Distributing a Mac Catalyst app requires that the app is provisioned using a provisioning profile. Provisioning profiles are files that contain code signing information, as well as the identity of the app and its intended distribution mechanism.
To distribute a .NET Multi-platform App UI (.NET MAUI) Mac Catalyst app outside the Mac App Store with ad-hoc distribution, you'll need to build a development provisioning profile specific to it. This profile enables the app to be digitally signed for release so that it can be installed on Macs. An ad-hoc development provisioning profile contains an App ID, a development certificate, and a list of the devices that can install the app. You'll need to create a development certificate to identify yourself or your organization, if you don't already have one.
The process for provisioning a .NET MAUI Mac Catalyst app for ad-hoc distribution is as follows:
- Create a certificate signing request. For more information, see Create a certificate signing request.
- Create a development certificate. For more information, see Create a development certificate.
- Create an App ID. For more information, see Create an App ID.
- Add devices to your Apple Developer Account. For more information, see Add devices.
- Create a provisioning profile. For more information, see Create a provisioning profile.
- Download your provisioning profile. For more information, see Download your provisioning profile in Xcode.
Important
This article shows provisioning for ad-hoc distribution using an Apple Developer Account that's part of the Apple Developer Program. It can also be used for an Apple Developer Account that's part of the Apple Developer Enterprise Program, as the steps required are largely identical.
Then, once provisioning is complete you should prepare your app for publishing, and then publish it with the following process:
- Optionally add entitlements to your app. For more information, see Add entitlements.
- Update the app's Info.plist file. For more information, see Update Info.plist.
- Publish your app using the command line. For more information, see Publish using the command line.
Create a certificate signing request
Before you create a distribution certificate, you'll first need to create a certificate signing request (CSR) in Keychain Access on a Mac:
On your Mac, launch Keychain Access.
In Keychain Access, select the Keychain Access > Certificate Assistant > Request a Certificate from a Certificate Authority... menu item.
In the Certificate Assistant dialog, enter an email address in the User Email Address field.
In the Certificate Assistant dialog, enter a name for the key in the Common Name field.
In the Certificate Assistant dialog, leave the CA Email Address field empty.
In the Certificate Assistant dialog, choose the Saved to disk radio button and select Continue:
Save the certificate signing request to a known location.
In the Certificate Assistant dialog, select the Done button.
Close Keychain Access.
Create a development certificate
The CSR allows you to generate a development certificate, which confirms your identity. The development certificate must be created using the Apple ID for your Apple Developer Account:
In a web browser, login to your Apple Developer Account.
In your Apple Developer Account, select the Certificates, IDs & Profiles tab.
On the Certificates, Identifiers & Profiles page, select the + button to create a new certificate.
On the Create a New Certificate page, select the Apple Development radio button before selecting the Continue button:
On the Create a New Certificate page, select Choose File:
In the Choose Files to Upload dialog, select the certificate request file you previously created (a file with a
.certSigningRequest
file extension) and then select Upload.On the Create a New Certificate page, select the Continue button:
On the Download Your Certificate page, select the Download button:
The certificate file (a file with a
.cer
extension) will be downloaded to your chosen location.On your Mac, double-click the downloaded certificate file to install the certificate to your keychain. The certificate appears in the My Certificates category in Keychain Access, and begins with Apple Development:
Note
Make a note of the full certificate name in Keychain Access. It will be required when signing your app.
Create a development profile
An ad-hoc development provisioning profile enables your .NET MAUI Mac Catalyst app to be digitally signed, so that it can be installed on specific Macs. An ad-hoc development provisioning profile contains an App ID, a development certificate, and a list of the devices that can install the app.
Create an App ID
An App ID is required to identify the app that you are distributing. An App ID is similar to a reverse-DNS string, that uniquely identifies an app, and should be identical to the bundle identifier for your app. You can use the same App ID that you used when deploying your app to a device for testing.
There are two types of App ID:
- Wildcard. A wildcard App ID allows you to use a single App ID to match multiple apps, and typically takes the form
com.domainname.*
. A wildcard App ID can be used to distribute multiple apps, and should be used for apps that do not enable app-specific capabilities. - Explicit. An explicit App ID is unique to a single app, and typically takes the form
com.domainname.myid
. An explicit App ID allows the distribution of one app, with a matching bundle identifier. Explicit App IDs are typically used for apps that enable app-specific capabilities such as Apple Pay, or Game Center. For more information about capabilities, see Capabilities.
To create a new App ID:
In your Apple Developer Account, navigate to Certificates, IDs & Profiles.
On the Certificates, Identifiers & Profiles page, select the Identifiers tab.
On the Identifiers page, select the + button to create a new App ID.
On the Register a new identifier page, select the App IDs radio button before selecting the Continue button:
On the Register a new identifier page, select App before selecting the Continue button:
On the Register an App ID page, enter a description, and select either the Explicit or Wildcard Bundle ID radio button. Then, enter the Bundle ID for your app in reverse DS format:
Important
The Bundle ID you enter must correspond to the Bundle identifier in the Info.plist file in your app project.
The bundle identifier for a .NET MAUI app is stored in the project file as the Application ID property. In Visual Studio, in Solution Explorer right-click on your .NET MAUI app project and select Properties. Then, navigate to the MAUI Shared > General tab. The Application ID field lists the bundle identifier.
When the value of the Application ID field is updated, the value of the Bundle identifier in the Info.plist will be automatically updated.
On the Register an App ID page, select any capabilities that the app uses. Any capabilities must be configured both on this page and in the Entitlements.plist file in your app project. For more information see Capabilities and Entitlements.
On the Register an App ID page, select the Continue button.
On the Confirm your App ID page, select the Register button.
Add devices
When creating a provisioning profile for ad-hoc distribution, the profile must include which devices can run the app. Before selecting the devices to be added to the provisioning profile you must first add devices to your Apple Developer Account. This can be achieved with the following steps:
Select the Apple > About this Mac menu item.
In the Overview tab, select the System Report... button.
In the System Report, select the Hardware expander to see the hardware overview. The report displays the universally unique identifier (UUID) as Hardware UUID in macOS 10.15 and earlier, or Provisioning UDID in macOS 11.0 and later.
Select the Hardware UUID or Provisioning UDID value and copy it to the clipboard.
In a web browser, go to the Devices section of your Apple Developer Account and click the + button.
In the Register a New Device page, set the Platform to macOS and provide a name for the new device. Then paste the identifier from the clipboard into the Device ID (UUID) field, and click the Continue button:
In the Register a New Device page, review the information and then click the Register button.
Repeat the above steps for any Macs that you want to deploy your .NET MAUI Mac Catalyst app to using ad-hoc distribution.
Create a provisioning profile
Once the App ID has been created, you should create a development provisioning profile. This profile enables the app to be digitally signed so that it can be installed on specific Macs.
To create a provisioning profile for ad-hoc distribution:
In the Certificates, Identifiers & Profiles page of your Apple Developer Account, select the Profiles tab.
In the Profiles tab, click the + button to create a new profile.
In the Register a New Provisioning Profile page, select the macOS App Development radio button before clicking the Continue button:
In the Generate a Provisioning Profile page, select the Mac Catalyst radio button. Then, in the App ID drop-down, select the App ID that you previously created before clicking the Continue button:
In the Generate a Provisioning Profile page, select the check box that corresponds to your development certificate before clicking the Continue button:
In the Generate a Provisioning Profile page, select the devices that the app will be installed on and then click the Continue button.
In the Generate a Provisioning Profile page, enter a name for the provisioning profile before clicking the Generate button:
Note
Make a note of the provisioning profile name, as it will be required when signing your app.
In the Generate a Provisioning Profile page, optionally click the Download button to download your provisioning profile.
Note
It's not necessary to download your provisioning profile now. Instead, you will do this in Xcode.
Download your provisioning profile in Xcode
After creating a provisioning profile in your Apple Developer Account, Xcode can download it so that it's available for signing your app:
On your Mac, launch Xcode.
In Xcode, select the Xcode > Preferences... menu item.
In the Preferences dialog, select the Accounts tab.
In the Accounts tab, click the + button to add your Apple Developer Account to Xcode:
In the account type popup, select Apple ID and then click the Continue button:
In the sign in popup, enter your Apple ID and click the Next button.
In the sign in popup, enter your Apple ID password and click the Next button:
In the Accounts tab, click the Manage Certificates... button to ensure that your distribution certificate has been downloaded.
In the Accounts tab, click the Download Manual Profiles button to download your provisioning profiles:
Wait for the download to complete and then close Xcode.
Add entitlements
Apple's App Sandbox restricts access to system resources and user data in Mac apps, to contain damage if an app becomes compromised. It must be enabled for Mac Catalyst apps that are distributed through the Mac App Store, and can optionally be enabled for Mac Catalyst apps that are distributed outside the Mac App Store.
This can be accomplished by adding an Entitlements.plist file to the Platforms/MacCatalyst folder of your .NET MAUI app project:
<?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.security.app-sandbox</key>
<true/>
</dict>
</plist>
The App Sandbox entitlement is defined using the com.apple.security.app-sandbox
key, of type boolean
. For information about App Sandbox, see Protecting user data with App Sandbox on developer.apple.com. For information about the App Sandbox entitlement, see App Sandbox Entitlement.
If your app opens outgoing network connections, you'll also need to add the com.apple.security.network.client
key, of type boolean
, to your Entitlements.plist file:
<?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.security.app-sandbox</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>
For information about the outgoing network connections entitlement, see com.apple.security.network.client on developer.apple.com.
Update Info.plist
Before distributing your app, you should update its Info.plist file with additional information.
Note
While it's not a requirement to update your app's Info.plist file when distributing it for testing, these updates will be required when distributing the final tested version of your app. Therefore, it's best practice to perform these updates.
Specify the user interface idiom
A Mac Catalyst app can run in the iPad or Mac user interface idiom:
- The iPad user interface idiom tells macOS to scale the app's user interface to match the Mac display environment while preserving iPad-like appearance.
- The Mac user interface idiom doesn't scale the app's user interface to match the Mac display environment. Some controls change their size and appearance, and interacting with them feels identical to interacting with
AppKit
controls.
By default, .NET MAUI Mac Catalyst apps use the iPad user interface idiom. If this is your desired behavior, ensure that the app's Info.plist file only specifies 2 as the value of the UIDeviceFamily
key:
<key>UIDeviceFamily</key>
<array>
<integer>2</integer>
</array>
To adopt the Mac user interface idiom, update the app's Info.plist file to specify 6 as the value of the UIDeviceFamily
key:
<key>UIDeviceFamily</key>
<array>
<integer>6</integer>
</array>
For more information about Mac Catalyst user interface idioms, see Specify the UI idiom for your Mac Catalyst app.
Set the default language and region for the app
Set the CFBundleDevelopmentRegion
key in your app's Info.plist to a string
that represents the localization native development region:
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
The value of the key should be a language designator, with an optional region designator. For more information, see CFBundleDevelopmentRegion on developer.apple.com.
Set the copyright key
Set the NSHumanReadableCopyright
key in your app's Info.plist to a string
that represents the human-readable copyright notice for your app:
<key>NSHumanReadableCopyright</key>
<string>MyMauiApp © 2023</string>
For more information, see NSHumanReadableCopyright on developer.apple.com.
Declare your app's use of encryption
If your app uses encryption, and you plan to distribute it outside the United States or Canada, it's subject to US export compliance requirements. You can provide information about you app's use of encryption in its Info.plist file.
This is accomplished by adding the ITSAppUsesNonExemptEncryption
key to your app's Info.plist with a boolean
value that indicates whether your app uses encryption:
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
For more information, see Complying with Encryption Export Regulations on developer.apple.com.
Publish using the command line
To publish your Mac Catalyst app from the command line on a Mac, open a terminal and navigate to the folder for your .NET MAUI app project. Run the dotnet publish
command, providing the following parameters:
Parameter | Value |
---|---|
-f or --framework |
The target framework, which is net8.0-maccatalyst . |
-c or --configuration |
The build configuration, which is Release . |
-p:MtouchLink |
The link mode for the project, which can be None , SdkOnly , or Full . |
-p:CreatePackage |
Set to true so that a package (.pkg) is created for the app at the end of the build. |
-p:EnableCodeSigning |
Set to true so that code signing is enabled. |
-p:CodesignKey |
The name of the code signing key. Set to the name of your distribution certificate, as displayed in Keychain Access. |
-p:CodesignProvision |
The provisioning profile to use when signing the app bundle. |
-p:CodesignEntitlements |
The path to the entitlements file that specifies the entitlements the app requires. Set to Platforms\MacCatalyst\Entitlements.plist . |
-p:RuntimeIdentifier |
The runtime identifier (RID) for the project. Release builds of .NET MAUI Mac Catalyst apps default to using maccatalyst-x64 and maccatalyst-arm64 as runtime identifiers, to support universal apps. To support only a single architecture, specify maccatalyst-x64 or maccatalyst-arm64 . |
-p:UseHardenedRuntime |
Set to true to enable the hardened runtime, which is required for Mac Catalyst apps that are distributed outside of the Mac App Store. |
Warning
Attempting to publish a .NET MAUI solution will result in the dotnet publish
command attempting to publish each project in the solution individually, which can cause issues when you've added other project types to your solution. Therefore, the dotnet publish
command should be scoped to your .NET MAUI app project.
Additional build parameters can be specified on the command line, if they aren't provided in a <PropertyGroup>
in your project file. The following table lists some of the common parameters:
Parameter | Value |
---|---|
-p:ApplicationTitle |
The user-visible name for the app. |
-p:ApplicationId |
The unique identifier for the app, such as com.companyname.mymauiapp . |
-p:ApplicationVersion |
The version of the build that identifies an iteration of the app. |
-p:ApplicationDisplayVersion |
The version number of the app. |
-p:RuntimeIdentifier |
The runtime identifier (RID) for the project. Release builds of .NET MAUI Mac Catalyst apps default to using maccatalyst-x64 and maccatalyst-arm64 as runtime identifiers, to support universal apps. To support only a single architecture, specify maccatalyst-x64 or maccatalyst-arm64 . |
For a full list of build properties, see Project file properties.
Important
Values for all of these parameters don't have to be provided on the command line. They can also be provided in the project file. When a parameter is provided on the command line and in the project file, the command line parameter takes precedence. For more information about providing build properties in your project file, see Define build properties in your project file.
For example, use the following command to build and sign a .pkg on a Mac, for ad-hoc distribution to users on registered devices:
dotnet publish -f net8.0-maccatalyst -c Release -p:MtouchLink=SdkOnly -p:CreatePackage=true -p:EnableCodeSigning=true -p:CodesignKey="Apple Development: John Smith (AY2GDE9QM7)" -p:CodesignProvision="MyMauiApp (Ad-hoc)" -p:CodesignEntitlements="Platforms\MacCatalyst\Entitlements.plist" -p:UseHardenedRuntime=true
Note
In .NET 8, the dotnet publish
command defaults to the Release
configuration. Therefore, the build configuration can be omitted from the command line.
Publishing builds, signs, and packages the app, and then copies the .pkg to the bin/Release/net8.0-maccatalyst/publish/ folder. If you publish the app using only a single architecture, it will be published to the bin/Release/net8.0-maccatalyst/{architecture}/publish/ folder.
During the signing process it maybe necessary to enter your login password and allow codesign
to run:
For more information about the dotnet publish
command, see dotnet publish.
Define build properties in your project file
An alternative to specifying build parameters on the command line is to specify them in your project file in a <PropertyGroup>
. The following table lists some of the common build properties:
Property | Value |
---|---|
<ApplicationTitle> |
The user-visible name for the app. |
<ApplicationId> |
The unique identifier for the app, such as com.companyname.mymauiapp . |
<ApplicationVersion> |
The version of the build that identifies an iteration of the app. |
<ApplicationDisplayVersion> |
The version number of the app. |
<CodesignKey> |
The name of the code signing key. Set to the name of your distribution certificate, as displayed in Keychain Access. |
<CodesignEntitlements> |
The path to the entitlements file that specifies the entitlements the app requires. Set to Platforms\MacCatalyst\Entitlements.plist . |
<CodesignProvision> |
The provisioning profile to use when signing the app bundle. |
<CreatePackage> |
Set to true so that a package (.pkg) is created for the app at the end of the build. |
<EnableCodeSigning> |
Set to true so that code signing is enabled. |
<MtouchLink> |
The link mode for the project, which can be None , SdkOnly , or Full . |
<RuntimeIdentifier> |
The runtime identifier (RID) for the project. Release builds of .NET MAUI Mac Catalyst apps default to using maccatalyst-x64 and maccatalyst-arm64 as runtime identifiers, to support universal apps. To support only a single architecture, specify maccatalyst-x64 or maccatalyst-arm64 . |
<UseHardenedRuntime> |
Set to true to enable the hardened runtime, which is required for Mac Catalyst apps that are distributed outside of the Mac App Store. |
For a full list of build properties, see Project file properties.
Important
Values for these build properties don't have to be provided in the project file. They can also be provided on the command line when you publish the app. This enables you to omit specific values from your project file.
The following example shows a typical property group for building and signing your Mac Catalyst app for ad-hoc distribution to users on registered devices:
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0-maccatalyst|AnyCPU'">
<MtouchLink>SdkOnly</MtouchLink>
<EnableCodeSigning>True</EnableCodeSigning>
<CreatePackage>true</CreatePackage>
<CodesignKey>Apple Development: John Smith (AY2GDE9QM7)</CodesignKey>
<CodesignProvision>MyMauiApp (Ad-hoc)</CodesignProvision>
<CodesignEntitlements>Platforms\MacCatalyst\Entitlements.plist</CodesignEntitlements>
<UseHardenedRuntime>true</UseHardenedRuntime>
</PropertyGroup>
This example <PropertyGroup>
adds a condition check, preventing the settings from being processed unless the condition check passes. The condition check looks for two items:
- The build configuration is set to
Release
. - The target framework is set to something containing the text
net8.0-maccatalyst
. - The platform is set to
AnyCPU
.
If any of these conditions fail, the settings aren't processed. More importantly, the <CodesignKey>
and <CodesignProvision>
settings aren't set, preventing the app from being signed.
After adding the above property group, the app can be published from the command line on a Mac by opening a terminal and navigating to the folder for your .NET MAUI app project. Then, run the following command:
dotnet build -f net8.0-maccatalyst -c Release
Publishing builds, signs, and packages the app, and then copies the .pkg to the bin/Release/net8.0-maccatalyst/publish/ folder.
Distribute your app for testing
The .pkg can be distributed to users on registered devices, where it can be run by double-clicking on the .pkg file to install the app.
For information about safely opening apps on a Mac, see Open apps safely on your Mac on support.apple.com.
See also
- Distributing your app to registered devices on developer.apple.com
- Preparing your app for distribution on developer.apple.com
- Hardened Runtime on developer.apple.com