App Framework
An app framework allows you to write an app, simplifying common topics, such as installers, GUIs, etc. Furthermore an app framework allows you to write an app once and build it for several platforms, e.g. Windows, macOS, Linux, Android, iOS, etc., without having to write the same app several times.
The app framework recommended by this guide is Tauri. Tauri supports building your app for Windows, macOS, Linux, Android and iOS. In this guide we will target all of those platforms.
Setup
Install Dependencies
General Dependencies
To use Tauri, you must first install some general dependencies by running the below commands:
sudo dnf install webkit2gtk4.1-devel openssl-devel curl wget file libappindicator-gtk3-devel librsvg2-devel
sudo dnf group install "c-development"
Rust
Tauri requires Rust to be installed. To install Rust, run the below command:
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
When prompted about the installation options, just hit Enter to proceed with the standard installation. Once the installation completes, re-open your terminal so that your PATH environment variable is reloaded. Your PATH should now include the Rust related binaries, e.g. cargo.
Node.js
To use a frontend JavaScript framework with Tauri, we will need to install Node.js. The frontend framework we will be using is Svelte. We have already previously installed Node.js and npm on our development machine in the ESLint section of this guide.
Bun
To use Bun as our package manager we need to install it:
curl -fsSL https://bun.sh/install | bash
The Bun installation should have appended the Bun installation path to our PATH in .bashrc. For this change to take effect, either re-open your terminal or run the below to reload your .bashrc file:
source ~/.bashrc
Android
Android Studio Installation
To target Android in Tauri, we need to have Android Studio installed on our development machine. To install Android Studio, download the latest release of Android Studio for your operating system from the Android Studio website.
Now install some required 32-bit libraries:
sudo dnf install ncurses-libs.i686 bzip2-libs.i686
The official Android Studio installation instructions also list "zlib.i686" as a package to install on Fedora, however this package doesn't seem to exist. The installation instructions seem out of date, so I'm not even sure if any of the 32-bit libraries listed above are required anymore, however there is also no harm in installing them anyway.
Extract the Android Studio archive you downloaded earlier:
cd ~/Downloads
sudo tar -xvf android-studio-2024.2.2.14-linux.tar.gz -C /opt/
Android Studio should now be installed in the /opt/android-studio directory. Now start Android Studio:
cd /opt/android-studio/bin
./studio
Android Studio should now launch. Click "Next" on the dialog:
Select "Standard" for the type of setup and click "Next":
On the next screen click "Next":
Read the license agreement and check "Accept" if you agree. Then click "Next":
Some information about KVM is displayed. We will install KVM on our development machine for enhanced Android emulator performance in a later section of this guide. For now click "Finish":
Android Studio should now download some required components:
Once the download completes, click "Finish":
Android Studio should now be ready. Open the "More Actions" accordion and select "SDK Manager":
In the SDK Manager go to the "SDK Tools" tab and select the "NDK" and "Android SDK Command-line Tools" items. Then press "Apply". You also need an "Android SDK Platform" (available from the "SDK Platforms" tab), "Android SDK Platform Tools" and "Android SDK Build-Tools", however these should already be installed.
Confirm the changes:
Read the license agreements, check "Accept" if you accept and then click "Next":
The additional software should now download:
Once done click "Finish":
KVM
For maximum performance of the Android emulator, install KVM on your system by running the below command:
sudo dnf install bridge-utils libvirt virt-install qemu-kvm
sudo dnf install libvirt-devel virt-top libguestfs-tools guestfs-tools
Start and enable the KVM daemon. The KVM daemon should now automatically launch on every boot:
sudo systemctl start libvirtd
sudo systemctl enable libvirtd
KDE Shortcut
To add a KDE shortcut for Android Studio create a new file named "android-studio.desktop" at the location "~/.local/share/applications/". Populate this file with the below content:
[Desktop Entry]
Categories=Development
Comment=Android Studio IDE
Exec=/opt/android-studio/bin/studio
Icon=/opt/android-studio/bin/studio.png
Name=Android Studio
Terminal=false
Type=Application
Now you should be able to launch Android Studio from the KDE start menu:
Environment Variables
For Tauri to be able to find and use your Android Studio installation, you need to set some environment variables. Append the below to your .bashrc file located in your home directory:
export JAVA_HOME=/opt/android-studio/jbr
export ANDROID_HOME="$HOME/Android/Sdk"
export NDK_HOME="$ANDROID_HOME/ndk/$(ls -1 $ANDROID_HOME/ndk)"
For more information about your bash configuration file, see the Configuration section on the CLI page of this guide.
Android Targets
To install the Android targets for Rust, run the below command:
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android
Create Project
We will now create a new Tauri project. First, we start by creating a new directory named "app" in the root of our Git repository:
mkdir app
Then we navigate into this new directory:
cd app
Inside the app directory we will use the create-tauri-app tool to generate a new Tauri project:
sh <(curl https://create.tauri.app/sh) .
When prompted, choose a project name and identifier. For the rest of the questions, follow the answers provided in the below screenshot:
We will be using TypeScript, Bun and Svelte in our Tauri app.
Run Project
To run your new Tauri project, we first need to install dependencies by running:
bun install
Now we can launch the Tauri app by running:
bun tauri dev
Tauri should build your project and start your app:
Tip
Unfortunately there is currently an issue which may cause a Tauri App to display a blank screen on app startup in Linux.
Fortunately there is a workaround. Append the following to your .bashrc file:
export WEBKIT_DISABLE_DMABUF_RENDERER=1
Then either re-open your terminal or run the below to reload your .bashrc:
source ~/.bashrc
Now when you launch the Tauri app again it should no longer display a blank screen.
To read more about this issue see this Tauri bug ticket and this Restic Browser bug ticket.
To run your project in an Android emulator, start by initializing the project for Android:
bun tauri android init
Now we need to start an emulator. To do so, open Android Studio, open the "More Actions" accordion and select "Virtual Device Manager":
Now start one of your Android emulated devices by clicking on the button with the "Play" icon. Optionally you can also create new Android emulated devices.
Now your Android emulator should have launched:
To run your Tauri app on the Android emulator, run:
bun tauri android dev
Your Tauri app should now be running on the Android emulator:
Linting
To lint the Rust code in our Tauri project we will use Clippy. To lint TypeScript code, we will use ESLint.
Clippy
To use Clippy, we need to install it by running the below command:
rustup component add clippy
Note that Clippy might already be installed. To lint your Tauri project, navigate to the "src-tauri" directory in your Tauri project and run the below command:
cargo clippy
ESLint
To use ESLint in your Tauri project, run the below command in the root directory of your Tauri project to add ESLint and required packages as dependencies to your project:
bun add -D eslint @eslint/js typescript typescript-eslint
In the root directory of your Tauri project, create a file named "eslint.config.mjs" and populate it with the below content:
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.recommended
);
Now we will add an npm script for linting TypeScript code. To do so, open package.json and add the following lines in the scripts section:
"lint": "eslint src",
"lint:fix": "eslint src --fix",
You can now lint your TypeScript code by running:
bun run lint
Building
To build your Tauri project, run:
bun tauri build
Tip
If your build fails with the following error message:
Error failed to bundle project:
failed to run linuxdeploy
Then add the below to your .bashrc file:
export ARCH=x86_64
export NO_STRIP=true
The ARCH should be set to whatever is your development machine's CPU architecture. In my case it is x86_64. After sourcing your .bashrc file the build should now succeed.
After a successful build, you can find the build artifacts in "src-tauri/target/release/bundle/":
By default, Tauri only builds for your development machine's platform, i.e. in this case for Linux x86_64. To build for other platforms we need to either run the build on the same platform as the desired target platform or use cross-compilation.
On our development machine, we will only build development builds for Linux x86_64 and for Android.
In the CI we will natively build binaries/installers/packages for macOS, Linux x86_64 and Linux ARM64. For the Windows, Android and iOS builds we will use cross-compilation.
To build on our development machine for Android, run the below command:
bun tauri android build
After a successful Android build, you can find the build artifacts in "src-tauri/target/":
Testing
Unit Tests
To run Rust unit tests, run the below command in the "src-tauri" directory:
cargo test
Naturally you don't have any Rust tests yet. We will add them in a later section of this guide.
To run TypeScript tests, we will first need to install the Jest testing framework. Inside your Tauri project's root directory, run the below command:
bun add -D jest ts-jest @types/jest
Now add a "test" script in package.json to run jest:
"test": "jest --passWithNoTests"
You can now run TypeScript unit tests by running:
bun run test
Naturally you don't have any TypeScript tests yet. We will add them in a later section of this guide.
Integration Tests
To run all Rust integration tests, the same command as for the unit tests is used. Run the below command in the "src-tauri" directory of your Tauri project:
cargo test
To run all TypeScript integration tests, the same command as for the unit tests is used. From the root directory of your Tauri project, run:
bun run test
E2E Tests
As our Tauri app is targets multiple platforms, we need to test on each platform. For more information about E2E testing our Tauri app, see the E2E Tests section on the Testing page in this guide.
Code Coverage
Rust
To see how much of our Rust production code is covered by tests we will use a code coverage tool called Tarpaulin. To install Tarpaulin, run:
cargo install cargo-tarpaulin
You can now get a code coverage report for your Rust code in your project by running the below command in the "src-tauri" directory:
cargo tarpaulin
TypeScript
To see how much of our TypeScript production code is covered by tests we will use Jest. With the correct configuration, Jest not only runs the tests but also generates a code coverage report.
Start by adding some necessary dependencies to your Tauri project. Run the below command in the root directory of your Tauri project:
bun add -D jest-environment-jsdom ts-node
Now generate an initial configuration file for Jest. Run the below command:
npm init jest@latest
On the prompts, give answers as in the below screenshot:
When you now run the below command, you should also get a code coverage report:
bun run test
Since we don't have any Jest tests yet, no code coverage report is generated. We will add Jest tests in a later section of this guide. Later we will also set coverage thresholds, so that "bun run test" will fail if the code coverage is too low. Jest may save coverage data in a directory named "coverage". We need to add this directory to the .gitignore file in the root directory of our Tauri project so that we don't accidentally add any of those coverage files to our Git repository. Append the below line to your .gitignore in the root directory of your Tauri project:
coverage
CI
As we will now add new linting, building and testing stages, it might make sense to remain our existing lint, build and test stages to explicilty mark them as being for web only. Rename the existing pipeline stages from "lint", "build" and "test" to "lint-web", "build-web" and "test-web" respectively. This will require changes in .gitlab-ci.yml, lint.yml, build.yml and test.yml.
Lint Stage
To lint our Tauri app, we will add a new linting stage to our CI pipeline. Modify your .gitlab-ci.yml file to include the below:
stages:
- lint-app
include:
- local: "ci/lint-app.yml"
In the "ci" directory of your Git repository, add a file named "lint-app.yml" and add the below content:
lint-app:
before_script:
- curl -fsSL https://bun.sh/install | bash
- source ~/.bashrc
- rustup component add clippy
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
image: ivangabriele/tauri:fedora-40-20
script:
- cd app
- bun install --no-save
- bun run lint
- cd src-tauri
- cargo clippy
stage: lint-app
Build Stage
Windows
To add a Windows build stage to your CI, modify your .gitlab-ci.yml as per the below:
stages:
- build-windows
include:
- local: "ci/build-windows.yml"
In the "ci directory of your Git repository, add a file named "build-windows.yml" and add the below content:
build-windows:
artifacts:
paths:
- app/src-tauri/target/x86_64-pc-windows-msvc/release/epic-fantasy-forge.exe
- app/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*.exe
before_script:
- curl -fsSL https://bun.sh/install | bash
- source ~/.bashrc
- dnf install -y mingw64-nsis
- wget https://github.com/tauri-apps/binary-releases/releases/download/nsis-3/nsis-3.zip
- unzip nsis-3.zip
- cp nsis-3.08/Stubs/* /usr/share/nsis/Stubs/
- cp -r nsis-3.08/Plugins/** /usr/share/nsis/Plugins/
- dnf install -y lld llvm clang
- rustup target add x86_64-pc-windows-msvc
- cargo install --locked cargo-xwin
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
image: ivangabriele/tauri:fedora-40-20
script:
- cd app
- bun install --no-save
- npm run tauri build -- --runner cargo-xwin --target x86_64-pc-windows-msvc
stage: build-windows
macOS
To add a macOS build stage to your CI, modify your .gitlab-ci.yml as per the below:
stages:
- build-macos
include:
- local: "ci/build-macos.yml"
In the "ci directory of your Git repository, add a file named "build-macos.yml" and add the below content:
build-macos:
artifacts:
paths:
- app/src-tauri/target/release/bundle/macos/*.app
- app/src-tauri/target/release/bundle/dmg/*.dmg
before_script:
- curl -fsSL https://bun.sh/install | bash
- export PATH=$PATH:~/.bun/bin
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
script:
- cd app
- bun install --no-save
- bun tauri build
stage: build-macos
tags:
- saas-macos-large-m2pro
Tip
The GitLab hosted macOS runners are currently only available for Premium and Ultimate GitLab users. If your pipeline fails with the error:
No matching runner available
Then it may be because you are using the free version of GitLab. In that case you would either need to upgrade to Premium or Ultimate, or setup a GitLab runner on your own Mac if you have one.
Linux x86_64
To add a Linux x86_64 build stage to your CI, modify your .gitlab-ci.yml as per the below:
stages:
- build-linux-x86-64
include:
- local: "ci/build-linux-x86-64.yml"
In the "ci directory of your Git repository, add a file named "build-linux-x86-64.yml" and add the below content:
build-linux-x86-64:
artifacts:
paths:
- app/src-tauri/target/release/epic-fantasy-forge
- app/src-tauri/target/release/bundle/appimage/*.AppImage
- app/src-tauri/target/release/bundle/deb/*.deb
- app/src-tauri/target/release/bundle/rpm/*.rpm
before_script:
- curl -fsSL https://bun.sh/install | bash
- source ~/.bashrc
- dnf install -y xdg-utils
- export ARCH=x86_64
- export NO_STRIP=true
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
image: ivangabriele/tauri:fedora-40-20
script:
- cd app
- bun install --no-save
- bun tauri build
stage: build-linux-x86-64
Linux ARM64
To add a Linux ARM64 build stage to your CI, modify your .gitlab-ci.yml as per the below:
stages:
- build-linux-arm64
include:
- local: "ci/build-linux-arm64.yml"
In the "ci directory of your Git repository, add a file named "build-linux-arm64.yml" and add the below content:
build-linux-arm64:
artifacts:
paths:
- app/src-tauri/target/release/epic-fantasy-forge
- app/src-tauri/target/release/bundle/appimage/*.AppImage
- app/src-tauri/target/release/bundle/deb/*.deb
- app/src-tauri/target/release/bundle/rpm/*.rpm
before_script:
- dnf install -y xdg-utils
- dnf install -y webkit2gtk4.1-devel openssl-devel curl wget file libappindicator-gtk3-devel librsvg2-devel
- dnf group install -y "c-development"
- dnf install -y nodejs npm
- dnf install -y unzip
- curl -fsSL https://bun.sh/install | bash
- curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y
- source ~/.bashrc
- export ARCH=aarch64
- export NO_STRIP=true
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
image: fedora:latest
script:
- cd app
- bun install --no-save
- bun tauri build
stage: build-linux-arm64
tags:
- saas-linux-small-arm64
Android
To build our Tauri app for Android in the CI, we will create our own Docker image. Start by creating a new directory named "docker" in the root of your Git repository:
mkdir docker
Inside this new directory create a file named "Dockerfile" and populate it with the below content:
FROM ivangabriele/tauri:fedora-40-20
ENV HOME=/root
ENV ANDROID_HOME=$HOME/cmdline-tools
ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk
ENV NDK_HOME=$HOME/ndk/29.0.13113456
ENV PATH=$PATH:~/.bun/bin
RUN curl -fsSL https://bun.sh/install | bash
RUN dnf install -y java-21-openjdk-devel.x86_64
RUN cd $HOME && \
rustup target add aarch64-linux-android armv7-linux-androideabi i686-linux-android x86_64-linux-android && \
wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip -O android-cli-tools.zip && \
unzip android-cli-tools.zip && \
cd cmdline-tools && \
mkdir latest && \
mv bin latest/ && \
mv lib latest/ && \
mv NOTICE.txt latest/ && \
mv source.properties latest/ && \
yes | $ANDROID_HOME/latest/bin/sdkmanager "platforms;android-35" "platform-tools" "ndk;29.0.13113456" "build-tools;35.0.0" && \
cp -r ~/licenses $ANDROID_HOME/.
To build this Docker image locally run:
docker build .
We will want to build this image in the CI so that when we change the Dockerfile a new Docker image is built automatically with the need for manual intervention. To add a Docker stage to your CI, modify your .gitlab-ci.yml as per the below. Place the new "docker-android" stage as the first stage in your CI pipeline.
stages:
- docker-android
include:
- local: "ci/docker-android.yml"
In the "ci" directory of your Git repository, add a file named "docker-android.yml" and add the below content:
docker-android:
image: docker:latest
script:
- cd docker
- echo "$CI_JOB_TOKEN" | docker login $CI_REGISTRY -u $CI_REGISTRY_USER --password-stdin
- docker build --pull -t $CI_REGISTRY_IMAGE:tauri-android .
- docker push $CI_REGISTRY_IMAGE:tauri-android
services:
- docker:dind
stage: docker-android
Tip
You don't need to manually set the CI variables CI_JOB_TOKEN, CI_REGISTRY, and CI_REGISTRY_USER. They are automatically set by GitLab to pull/push docker images from/to your GitLab container registry.
Now that we create a docker image to build Android in the CI, we can add an Android build stage to the CI. To do so, modify your .gitlab-ci.yml as per the below:
stages:
- build-android
include:
- local: "ci/build-android.yml"
In the "ci directory of your Git repository, add a file named "build-android.yml" and add the below content:
build-android:
artifacts:
paths:
- app/src-tauri/gen/android/app/build/outputs/apk/universal/release/*.apk
- app/src-tauri/gen/android/app/build/outputs/bundle/universalRelease/*.aab
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
image: $CI_REGISTRY/brusecke/epic-fantasy-forge:tauri-android
script:
- cd app
- bun install --no-save
- export PATH="$HOME/.bun/bin:$PATH"
- bun run tauri android build --target aarch64
stage: build-android
iOS
To build our iOS Tauri app in the CI, we must first initialize the project for iOS. We already did this for Android in the Run Project section above. To run this command, we will need a Mac.
If you don't have a Mac, you can temporarily rent one on Scaleway. A Mac mini M4 costs €0.22 per hour. As the minimum rental period is 24 hours, the total cost will be at mimum €5.28. It is not recommended to rent the Mac on a monthly basis as we will rarely need it.
Start by creating an account on Scaleway. Once you have an account, click on your username on the top right and select "SSH Keys":
Click on "Add SSH key":
On the form, choose a name for your SSH key. In the fingerprint field, copy and paste the contents of your development machine's public key. This can normally be found in a file located at ~/.ssh/id_rsa.pub". For more information about SSH keys, see the SSH Keys section on the Version Control page of this guide.
Your public SSH key should now be uploaded to Scaleway. We will now rent a Mac Mini. On the left sidebar, click on "Apple silicon" under the "Bare Metal" category:
Click "+ Create Mac mini":
Choose the appropriate options on the form to create your Mac Mini instance. Note that sometimes the "M4-S" instance is not available due to low stock. In this case you may need to choose a different type of Mac Mini.
Once you are done choosing the appropriate options on the form, read through the terms and conditions and license agreements linked at the bottom of the page. If you agree with the conditions then tick the relevant checkbox and click on "Create Mac mini":
Your Mac Mini instance should now be created:
Once it is done creating, your instance details should be shown. Click on the "SSH command" field to copy it and paste it into a terminal. Run the command in the terminal to remotely connect to your Mac Mini instance from your development machine. You should not be prompted for a password since you uploaded your public SSH key to Scaleway earlier.
Tip
Note that the Mac you created in Scaleway is not automatically deleted when you are done with it. You have to manually remove it from your Scaleway account when you are done using it, otherwise you will be continuously billed for the running Mac.
Developer tools such as Git and Xcode should already be pre-installed on the Mac Mini instances from Scaleway. We will need to configure Git on our instance and add an SSH keypair to it that is authorized to push to our Git repository. The reason for this is that we will initialize our Tauri project for iOS on this Mac instance and then push those changes to our Git repository.
Start by generating an SSH keypair for your Mac. Generate this keypair on your development machine and not the Mac instance. Follow the instructions in the SSH Key Creation section on the Version Control page of this guide. However this time give the generated keypair a custom name, for example "mac_id_rsa" for the private key and "mac_id_rsa.pub" for the public key:
Now upload the newly created keys to your Mac Mini instance:
You can find your Mac Mini's connection details on your Scaleway Console. You should have copied them earlier a few paragraphs above when your Mac Mini instance was created.
SSH into your Mac Mini instance using the connection details shown on the Scaleway Console:
On your Mac Mini, rename the public and private key to their default names:
Now add the Mac Mini public key to your GitLab authorized SSH keys. For more details how to do this see the Adding your Public SSH Key to GitLab section on the Version Control page of this guide.
Now clone your Git repository on the Mac Mini instance. Naturally replace the URL in the example command below with your Git repository's actual URL which you can find from GitLab. For more details about cloning your GitLab repository, see the Cloning a Git Repository section on the Version Control page of this guide.
git clone [email protected]:brusecke/epic-fantasy-forge.git
Navigate into your Tauri project:
cd epic-fantasy-forge/app
As our project depends on Bun, we need to install it:
curl -fsSL https://bun.sh/install | bash
Add Bun to your PATH:
echo 'export PATH="$HOME/.bun/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc
We also need to install Rust. When prompted, accept the default options by hitting Enter.
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
Add Rust to your PATH:
echo '. $HOME/.cargo/env' >> ~/.zshrc
source ~/.zshrc
Additionally we will need to install Node.js. We will install Node.js using the Homebrew package manager. First let's install the Homebrew package manager by running:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
When prompted for a sudo password, enter the password for your Mac Mini instance. You can find this password on the Scaleway Console:
For any other prompts just hit Enter to use the default options. Now add Homebrew to your PATH:
echo >> /Users/m1/.zprofile
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/m1/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
Update Homebrew:
brew update
Now install Node.js:
brew install node
Install the iOS targets:
rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
Install Cocoapods:
brew install cocoapods
Install the dependencies of your project:
bun install --no-save
Initialize the iOS target in your Tauri project:
bun tauri ios init
We now need to commit the new iOS project to our Git repository. Our Mac does not yet have a .gitconfig. Create one by following the instructions in the Git Configuration section on the Version Control page of this guide. Here is an example .gitconfig for your Mac:
[user]
name = Henrik Brüsecke
email = [email protected]
Now create a new branch in your Git repository and check it out:
git checkout -b ios-project-initialization
Now stage all changes in your project:
git add .
Commit the changes:
git commit -m "Initialize iOS project"
Push the changes:
git push origin ios-project-initialization
Before proceeding, create a merge request for the "ios-project-initialization" branch and merge it to the main branch.
To sign and distribute our iOS app, we need to enroll in the Apple Developer Program. The enrollment costs €99 per year. Start by creating an Apple Account.
Tip
You may need to create a new Apple Account from an Apple device. At least for me creation of an Apple Account failed from my Linux developer machine. Creation of a new Apple acocunt succeeded on the Mac Mini rented from Scaleway. See a little bit below for guidance on how to remotely access your Mac's GUI. You can then use your Mac's GUI to create a new Apple account if you don't have one already.
Once you have an Apple Developer account we need to connect the Scaleway Mac Mini to your Apple Account. Note that despite only using the Mac Mini temporarily, it is mandatory to connect it to our Apple account to proceed with the next steps.
On your Apple Developer account dashboard, click on "Devices":
Click on "Register a Device":
To register the Scaleway Mac Mini, we need its Device ID (UDID). For some parts of this guide we will need access to the GUI of the Mac, i.e. SSH access will not suffice. To find out the UDID, we will use the Mac's GUI.
To remotely access your Mac's GUI, you need a VNC client. In this guide we will use the Remmina VNC client. To install it run:
sudo dnf install remmina
Once it is installed, launch Remmina and enter your VNC connection details. You can find the VNC connection details for your Mac Mini on the Scaleway Console. You will need the Mac's IP address and VNC port:
In Remmina, select "VNC" for the connection type and fill in the IP address and VNC port in the following format:
You may be prompted for a username and password. Enter your Scaleway Mac Mini's username and password. You can find your Mac Mini's credentials from the Scaleway Console:
You should now be remotely logged into your Mac's GUI:
To find out the UDID of your Mac, click on the Apple icon on the top left of the screen and choose "About This Mac":
Click on "More info...":
Click on "System Report...":
You can now find your Mac's UDID in the "System Information" window in the field "Provisioning UDID". Use this field to populate the "Device ID" field on the "Register a New Device" form. On the form, choose "macOS" as the "Platform" and pick a "Device Name", e.g. "Scaleway Mac Mini". Once you have completed the form, click "Continue":
On the next screen click "Done":
The Mac Mini should now be registered with your Apple Developer account:
Now open your iOS project in Xcode. Start Xcode:
Click on "Open Existing Project..."
Navigate to the path where you previously cloned the Tauri project. From the root of your Tauri project, go inside the src-tauri/gen/apple directory and click "Open":
We will now log into your Apple Developer account from Xcode. Click on "Xcode" in the top left corner and choose "Settings...":
Select the "Accounts" tab and click the "+" icon to add your Apple Developer account:
On the next dialog select "Apple ID" and click "Continue" to proceed with the login.
After you have logged in, your Apple Developer account should be shown:
Now we need to configure signing of your app. Click on your project, click on your target and select the "Signing & Capabilities" tab. Check the "Automatically manage signing" checkbox. For the "Team", select the team from your Apple Developer account. You can leave the "Bundle Identifier" with the default value.
You can now verify that your app works by opening a terminal in your VNC session, navigating to your Tauri project's root directory and running:
bun tauri ios dev
When prompted for a simulator to use, pick your preffered choice, e.g. "iPhone 16 Pro Max". Tauri should now build your iOS app and launch the app on the emulator:
You can build your iOS project by running:
bun tauri ios build
Tip
If running the build command from an SSH session rather than a terminal opened in your VNC session, you need to run the below additional command for the build to succeed:
security unlock-keychain
When prompted for a password, enter your Scaleway Mac Mini's password.
You should again have unstaged changes in your Git repository since the project configuration changes we made in Xcode modified some of your project files. Commit these changes to your Git repository by running the below commands in your Tauri project directory:
git checkout -b ios-project-signing
git add .
git commit -m "iOS Project Signing"
git push origin ios-project-signing
Before proceeding, create a merge request for the "ios-project-signing" branch and merge it to the main branch.
To be able to use our Apple Developer account in the CI, we need to use create and App Store Connect API key. To do so, go to App Store Connect and click on "Users and Access":
Click on the "Integrations" tab and select the "App Store Connect API" key on the left sidebar. Click on "Request Access":
Read through the text on the dialog and check the checkbox if you agree. Then click "Submit":
Now we can generate an API key. Choose the "Team Keys" tab and click on "Generate API Key":
Give your new API key a name (e.g. "CI) and set the access level to "Admin". Then press "Generate":
You should now have an App Store Connect API key. Download the private key file by clicking on "Download":
Click "Download":
Once you have downloaded the private key, we must convert it to base64 format and remove the new lines:
base64 AuthKey_XXXXXXXXXX.p8 | tr -d '\n' | cat
"AuthKey_XXXXXXXXXX.p8" above is the name of the file you downloaded. After running the above command a base64 version of your App Store Connect private key should be printed to the console. Copy the value printed to the console.
We will now save this private key to the GitLab CI. Create a new CI variable with the key "APPLE_API_PRIVATE_KEY". Paste the value you copied earlier from the console into the "Value" field of the CI variable. Naturally set the "Visibility" to "Masked and hidden" since the key should be kept secret. Do not tick the "Protect variable" checkbox since we need this variable on non-protected branches also. For more information how to create CI variables see the CI Variables section on the Technical Documentation page of this guide.
Now add two additional CI variables:
- APPLE_API_ISSUER
- The value for this key can be found in the Issuer ID field visible on the "App Store Connect API" page (see below)
- APPLE_API_KEY
- The value for this key can be found in the "KEY ID" field visiblle on the "App Store Connect API" page (see below)
Finally you should have three Apple CI variables defined in your CI:
Now you can add an iOS build stage to your CI, modify your .gitlab-ci.yml as per the below:
stages:
- build-ios
include:
- local: "ci/build-ios.yml"
In the "ci directory of your Git repository, add a file named "build-ios.yml" and add the below content:
build-ios:
artifacts:
paths:
- app/src-tauri/gen/apple/build/arm64/*.ipa
before_script:
- curl -fsSL https://bun.sh/install | bash
- export PATH="$HOME/.bun/bin:$PATH"
- curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y
- . $HOME/.cargo/env
- NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
- echo >> /Users/gitlab/.zprofile
- echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> /Users/gitlab/.zprofile
- eval "$(/opt/homebrew/bin/brew shellenv)"
- brew update
- brew install node
- rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
- brew install cocoapods
- echo $APPLE_API_PRIVATE_KEY | base64 -d > /Users/gitlab/apple_api_private_key
- export APPLE_API_KEY_PATH=/Users/gitlab/apple_api_private_key
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
script:
- cd app
- bun install --no-save
- bun tauri ios build
stage: build-ios
tags:
- saas-macos-large-m2pro
Test Stage
To test our Tauri app, we will add a new testing stage to our CI pipeline. Modify your .gitlab-ci.yml file to include the below:
stages:
- test-app
include:
- local: "ci/test-app.yml"
In the "ci" directory of your Git repository, add a file named "test-app.yml" and add the below content:
test-app:
before_script:
- curl -fsSL https://bun.sh/install | bash
- source ~/.bashrc
- cargo install cargo-tarpaulin
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- app/src-tauri/target
- app/node_modules
image: ivangabriele/tauri:fedora-40-20
script:
- cd app
- bun install --no-save
- bun run test
- cd src-tauri
- cargo tarpaulin
stage: test-app
Visual Studio Integration
In our Tauri project we use ESLint to lint our TypeScript code, just like we already do in our Phoenix project. Installation of the ESLint Visual Studio extension is covered in the ESLint section on the Web Framework page of this guide.
Svelte
To add support for Svelte to Visual Studio Code, run the below command:
code --install-extension svelte.svelte-vscode
Rust
To add support for Rust to Visual Studio Code, run the below command:
code --install-extension rust-lang.rust-analyzer
Tauri
To add support for Tauri to Visual Studio Code, run the below command:
code --install-extension tauri-apps.tauri-vscode
Cleanup
Your auto-generated Tauri project contains a lot of demo code to display the default screen of the Tauri project. Let's remove all of this demo code so you have an empty screen instead of the demo screen displayed when you run the Tauri project.
Start by completely empyting the "+page.svelte" file located at "src/routes":
Next replace the demo title in "src/app.html" with your actual app's title:
<title>Epic Fantasy Forge</title>
Delete the below lines from "src-tauri/src/lib.rs":
#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}
.invoke_handler(tauri::generate_handler![greet])
Modify "src-tauri/tauri.conf.json" to have your app's name as the window title:
"title": "Epic Fantasy Forge",
Your cleaned app should now look like this:
Tailwind CSS
We will use Tailwind CSS in our Svelte frontend. To add Tailwind CSS, navigate to your Tauri project's root directory and run the below command:
bun add -D tailwindcss @tailwindcss/vite
Now add Tailwind CSS to your Vite configuration file:
import tailwindcss from '@tailwindcss/vite';
export default defineConfig(async () => ({
plugins: [sveltekit(), tailwindcss()],
In your Tauri project's "src" directory, create a file named "app.css" and populate it with the below content:
@import "tailwindcss";
Now take Tailwind CSS into use in your "+page.svelte" file which can be found under "src/routes":
<script>
import "../app.css";
</script>
<style lang="postcss">
@reference "tailwindcss";
</style>