Publishing Tauri to Apple's App Store

See short instructions here.

Step By Step Instructions

This is step by step instructions on how to create, sign and upload a Tauri application to the Apple Store. At the end of this, you should understand all the steps involved and then be able to successfully upload your own app. That is the goal at any rate. The first Tauri app I deployed to the Apple app store was an absolute nightmare. It took far longer to work out all the details than I'm willing to admit here. In the hopes of saving you many of wasted hours, here is what I finally did to get my f***ing app in the app store! You can skip all the detail and see the bare bone steps here.

Not Included

I will not cover things that need to be done on the Apple Dev / Apple Store side. I'll include some links and comments where needed but don't expect any details.

In 10 Steps

  • Create the Certificates
  • Create Provisioning File
  • Create entitlements.plist
  • Configure tauri.conf.json
  • Set Version Number
  • Create the App Bundle
  • Add Provisioning Profile
  • Sign the App Bundle
  • Create Signed Package
  • Upload

What You Need

Apple Account:
  • An Active Apple Account (duh)
2 Certificates:
  • Apple Distribution Certificate
  • 3rd Party Mac Developer Installer Certificate
Provisioning Profile File:
  • embedded.provisionprofile
Entitlements File:
  • entitlements.plist
App Bundle:
  • Your_App_Name.app
Applications:
  • Xcode (required)
  • Transporter (optional)

Xcode

Use Xcode. Yes, use Xcode. No matter what you do, you will need Xcode installed on your system. You'll need it to run the command line tools used here. You may also want to use it for creating and managing certificates. Once you've figured out where to go, it becomes a quick and easy process. Needed For:
  • Creating Certificates
  • Command Line Tools for Signing, Uploading

1 - Certificates

Use Xcode! I found Xcode the easiest way to create certificates. Defining them inside Xcode was the most pain free way to do it. You can create your certificates on your Apple account online, and then download them. However, I couldn't get this to work correctly. The certs didn't seem properly signed with Apple's certificate issuer. Maybe somebody smarter than me can tell me how to do it. Certificates:
  • Apple Development Certificate
  • 3rd Party Mac Developer Installer Certificate
Apple Development Certificate:
Signs the app bundle.
3rd Party Mac Developer Installer Certificate:
Signs the upload package.
From Xcode:
Menu: Xcode -> Settings... -> Accounts (Tab) -> Manage Certificates... (Button)

2 - Create Provisioning Profile

Create and download a provisioning profile for your application on your Apple dev account. Apple Dev: Your Provisioning Profiles

3 - Create Entitlements

You need to create and add an entitlements file to your .app bundle. Apple will use this to determine what privileges your app needs and this information becomes part of the application signature once signed. At a minimum you will need com.apple.security.app-sandbox and com.apple.security.network.client. Apple Docs: Entitlements File Name:
entitlements.plist
Sample Contents with Minimum Requirements:
<?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>

4 - Configure Tauri

Set up the following items in your tauri.conf.json file.
  • productName
  • version
  • copyright
  • identifier
  • entitlements
Identifier:
The app identifier set up by you in your Apple account under Certificates, Identifiers & Profiles. Apple Dev: Your Certificates, Identifiers & Profiles
Version:
The version number you set up in your Apple account. From the App settings, click + on the top left to add a new version. Apple Dev: Your Applications
Tauri Docs: Configuration File:
src-tauri/tauri.conf.json
Add These:
{ "package": { "productName": "App Name", "version": "1.2.3" }, "tauri": { "bundle": { "copyright": "Copyright © 2023 ...", "identifier": "io.domain.app-name", "macOS": { "entitlements": "../entitlements.plist", }, }, } }
Sample:
{ "package": { "productName": "Encode Escape", "version": "1.0.11" }, "tauri": { "bundle": { "copyright": "Copyright © 2023 ThinkGo LLC", "identifier": "io.thinkgo.encode-escape", "macOS": { "entitlements": "../entitlements.plist", }, }, } }

5 - Set / Update Version Number

Set up the version number to match the one you added on your App settings on your Apple account. Apple Dev: Your Applications Update these project files:
package.json src-tauri/Cargo.toml src-tauri/tauri.conf.json
package.json:
"version": "1.2.3",
src-tauri/Cargo.toml:
version = "1.2.3"
src-tauri/tauri.conf.json:
"version": "1.2.3"
package.json:
  • NPM Version Number
src-tauri/Cargo.toml:
  • Use By Cargo Build
  • Sets the rlib Version Number
src-tauri/tauri.conf.json:
  • About Version Number
  • Install Version Number
  • info.plist Version Number (MacOS)

6 a - Create the App Bundle

Skip if you already have your app bundle (Your_App_Name.app). The actual command depends on how you created and run your Tauri builds. Create the app bundle if using NPM:
npm run tauri build
Find Here in Your Project Directory:
"src-tauri/target/release/bundle/macos/Your_App_Name.app

6 b - Create the App Bundle - Universal Binary

Create a universal bundle if using NPM:
npm run tauri build -- --target universal-apple-darwin
Find It Here:
"/src-tauri/target/universal-apple-darwin/release/bundle/macos/Your_App_Name.app

7 - Add Provisioning Profile

Copy your provisioning file into your app bundle. File:
embedded.provisionprofile
Copy into the app bundle:
Your_App_Name.app/Contents/embedded.provisionprofile

8 - Sign The App Bundle

Sign the completed app bundle with your apple distribution certificate:
codesign \ --sign "APPLE_DISTRIBUTION_CERT_NAME" \ --entitlements "entitlements.plist" \ "Your_App_Name.app/Contents/MacOS/Your_App_Name"
Sample:
codesign \ --sign "Apple Distribution: ThinkGo LLC (12A3BC4DEF)" \ --entitlements "entitlements.plist" \ "Encode Escape.app/Contents/MacOS/Encode Escape"

9 - Create Signed Package

Create and sign the final package. This is what you will upload to the App Store.
productbuild \ --sign "3RD_PARTY_MAC_DEVELOPER_INSTALLER_CERT" \ --component "Your_App_Name.app" "/Applications" \ "Your_App_Name.pkg"
Sample:
productbuild \ --sign "3rd Party Mac Developer Installer: ThinkGo LLC (12A3BC4DEF)" \ --component "Encode Escape.app" "/Applications" \ "Encode Escape.pkg"

10 a - Upload with Transformer

Transformer is the easiest way to upload your app. As long as everything worked in the previous steps, Transformer will accept and upload your signed package file. It can be a bit finicky and doesn't always give good error messages. Steps:
  • Open Transporter.
  • Drag your package onto it.
  • Click 'Upload'
App Store: Apple Transporter App

10 b - Upload Using xcrun altool

Use xcrun altool if you need to automate the uploading to Apple. It is more work than Transporter. The trickiest part will be getting the app-bundle-version from Info.plist and passing it as a parameter to xcrun. apple-app-id - Get from your Apple account. app-bundle-version - Get from Info.plist inside the app bundle (CFBundleVersion property) App Bundle ID: From Info.plist:
<key>CFBundleVersion</key> <string>20230224.022525</string>
Command:
xcrun altool \ --upload-package "APP_PACKAGE" \ --type macos \ --apple-id "APP_APPLE_ID" \ --bundle-version "APP_BUNDLE_VERSION" \ --bundle-short-version-string "APP_VERSION" \ --bundle-id "APP_BUNDLE_ID" \ -u "USER" \ -p "PASSWORD"
Sample:
xcrun altool \ --upload-package "encode_escape_1.0.11.pkg" \ --type macos \ --apple-id "1665205132" \ --bundle-version "20230224.022525" \ --bundle-short-version-string "1.0.11" \ --bundle-id "io.thinkgo.encode-escape" \ -u "some-apple-id@thinkgo.io" \ -p "abcd-efgh-ijkl-mnop"

Everything, Everywhere, All at Once

All on a Bash Script:
# Set These: APP_NAME="Your App Name Here" PROJECT_DIR="/path/to/tauri/project" APP_APPLE_ID="1234567890" APP_BUNDLE_VERSION="20011231.120101" APP_VERSION="1.2.3" APP_BUNDLE_ID="com.domain.app-name" USER="your-apple-id@domain.com" PASSWORD="abcd-efgh-ijkl-mnop" $APPLE_DISTRIBUTION_CERT="Apple Distribution: Your Cert Name Here" $THIRD_PARTY_MAC_DEVELOPER_CERT="3rd Party Mac Developer Installer: Your Cert Name Here" # Change if needed: APP_BUNDLE="$APP_NAME.app" APP_EXECUTABLE="$APP_BUNDLE/Contents/MacOS/$APP_NAME" APP_PACKAGE="$APP_NAME.pkg" # Build, Sign, Package, Upload: npm run tauri build -- --target universal-apple-darwin cp "$PROJECT_DIR/src-tauri/target/universal-apple-darwin/release/bundle/macos/$APP_BUNDLE" . cp "embedded.provisionprofile" "$APP_BUNDLE/Contents/embedded.provisionprofile" codesign \ --sign "$APPLE_DISTRIBUTION_CERT" \ --entitlements "entitlements.plist" \ "$APP_EXECUTABLE" productbuild \ --sign "$THIRD_PARTY_MAC_DEVELOPER_CERT" \ --component "$APP_BUNDLE" "/Applications" \ "$APP_PACKAGE" xcrun altool \ --upload-package "$APP_PACKAGE" \ --type macos \ --apple-id "$APP_APPLE_ID" \ --bundle-version "$APP_BUNDLE_VERSION" \ --bundle-short-version-string "$APP_VERSION" \ --bundle-id "$APP_BUNDLE_ID" \ -u "$USER" \ -p "$PASSWORD"

Links

App Store: Apple Dev Account: Apple Docs: SpeedSheets:
Rust SpeedSheet Tauri SpeedSheet (a work in progress)

Terms

App Bundle

Your app bundled into a runnable package that ends in .app (your-app.app). This is actually a directory containing the executable, icons, and other information the Mac needs to run the application.

Bundle ID

The id of the application. Typically: com.domain.app-name.

Bundle Version

A unique version number. Tauri creates this number and embeds it in the Item.plist file. Tauri sets this to the date and time of the build. Sample: "20010101.120000"

Certificate - 3rd Party Mac Developer Installer Certificate

The certificate used to sign and create the final upload package. You create this in Xcode.

Certificate - Apple Distribution Certificate

The certificate used to sign the app bundle with the entitlements file. You create this in Xcode

embedded.provisionprofile

See: Previsioning Profile (below)

entitlements.plist

The file containing the entitlements or privileges Apple needs to grant your application to run. Xcode will use this when signing the app bundle.

Item.plist

Contains metadata about your application. This is generated by Tauri and lives in the app bundle. Where: your-app.app/Contents/Info.plist Includes: App Name, Bundle Id, Version Number, Bundle Version, Copyright Text, etc.

NPM

Node Package Manager

Provisioning Profile

Apple uses this to verify the app is legitimate and been created by authorized people. You create this in your account and download it, and then place it in your app bundle. It links the user to the app, the app's target environment, and to the certificate used to sign the app bundle. Where: your-app.app/Contents/embedded.provisionprofile

RLIB

Rust Library, the compiled intermediate library of your app.

Transporter

Apple's package upload manager. Use this. It will make your life easier unless you need to automate build uploads. Then use xcrun altool.

xcrun

Xcode's command line utility. This does most of the heavy lifting.