Tauri mobile for iOS
Tags: tutorial, ios, rust, typescript
Published: 2023-08-05 13:55:48

I've been recently experimenting with Tauri alpha to try out mobile app development. By following the official guide and resolving problems, this is how I managed to set up a basic project utilising web view.


## Fast forward template solution

It's highly recommended to follow this simplified guide including the project template.
You can find it here https://github.com/Adimac93/tauri-mobile-apple-ecosystem
Also, an android version (based on my template) is available here https://github.com/casey-SK/tauri-mobile-android
You may read along with this post if you are interested in particular set-up steps.

## MacOS

### XCode

![XCode](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/uouapxvl79enxahngs58.png)


I've tried to set up XCode both with the latest stable release (14.3.1) and the beta release (for IOS 17). However, only a stable release worked for me.

> ⚠️ Warning
> XCode from AppStore is known for notorious problems with downloading. 
> Instead download it from Apple website [XCode 14.3.1 download](https://download.developer.apple.com/Developer_Tools/Xcode_14.3.1/Xcode_14.3.1.xip).


You will need to make sure you have command line tools installed on your machine:
```bash
$ xcode-select --install
```
If you have multiple XCode CLI tool versions installed, you can set CLI version in XCode `Settings... > Locations > Command Line Tools`.

![XCode CLI version](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0ihal9v322rsc6a5wy5w.png)

You should log in in XCode with your Apple ID to sign apps with a personal certificate. This will enable you to test your apps directly on your device.

`Settings... > Accounts > + > Apple ID`
![XCode Apple ID](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3dybw6jouxlsgqw77ka1.png)

### Rust

Next dependency is Rust which can be downloaded by following command:
```bash
$ curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
```

If you have it already installed, make sure it's up to date:
```bash
$ rustup update
```

iOS targets
```bash
$ rustup target add aarch64-apple-ios x86_64-apple-ios aarch64-apple-ios-sim
```

### App setup
Using Tauri scaffolding utility it's easier to set up a new project with desired frontend framework.
In this example I'll use the following options:
- Frontend language: TypeScript
- Package manager: pnpm
- UI template: Svelte
- UI flavour: TypeScript


```bash
$ cargo install create-tauri-app
```

```bash
$ cargo create-tauri-app
```
![Console output](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e71kos5me7qufsx17rmd.png)

Install frontend dependencies:
```bash
$ cd tauri-mobile
$ pnpm add -D internal-ip
$ pnpm i
```

Update tauri CLI:
```bash
$ pnpm update @tauri-apps/cli@next @tauri-apps/api@next
```
and if you'd like to use CLI with cargo
```bash
$ cargo install tauri-cli --version "^2.0.0-alpha"
```

#### Vite

Default Vite config for Svelte generated by `create-tauri-app`.
`vite.config.ts`
```ts
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";

// https://vitejs.dev/config/
export default defineConfig(async () => ({
  plugins: [svelte()],
  clearScreen: false,
  server: {
    port: 1420,
    strictPort: true,
  },
  envPrefix: ["VITE_", "TAURI_"],
}));

```

We need to change it to the following:
`vite.config.ts`
```ts
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { internalIpV4 } from 'internal-ip'

// https://vitejs.dev/config/
export default defineConfig(async () => ({
  plugins: [svelte()],
  clearScreen: false,
  server: {
    host: "0.0.0.0",
    port: 5173,
    strictPort: true,
    hmr: {
      protocol: 'ws',
      host: await internalIpV4(),
      port: 5183,
    },
  },
  envPrefix: ["VITE_", "TAURI_"],
}));

```

#### Tauri

1. Remove `allowlist`
2. Match `devPath` port (same as `vite.config.ts > server > port`)
3. Change bundle identifier (use only alphanumeric characters because other chars are bugged right now)

> 💡 Tip
> A bundle ID uniquely identifies a single app throughout the system. The bundle ID string must contain only alphanumeric characters (A–Z, a–z, and 0–9), hyphens (-), and periods (.). Typically, you use a reverse-DNS format for bundle ID strings. Bundle IDs are case-insensitive.
> [Apple docs](https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier)

`src-tauri/tauri.conf.json`
```diff
{
  "build": {
    "beforeDevCommand": "pnpm dev",
    "beforeBuildCommand": "pnpm build",
-   "devPath": "http://localhost:1420",
+   "devPath": "http://localhost:5173",
    "distDir": "../dist",
    "withGlobalTauri": false
  },
  "package": {
    "productName": "tauri-mobile",
    "version": "0.0.0"
  },
  "tauri": {
-   "allowlist": {
-     "all": false,
-     "shell": {
-       "all": false,
-       "open": true
-     }
-   },
    "bundle": {
      "active": true,
      "targets": "all",
-     "identifier": "com.tauri.dev",
+     "identifier": "com.<YOUR ID>.dev"
      "icon": [
        "icons/32x32.png",
        "icons/128x128.png",
        "icons/128x128@2x.png",
        "icons/icon.icns",
        "icons/icon.ico"
      ]
    },
    "security": {
      "csp": null
    },
    "windows": [
      {
        "fullscreen": false,
        "resizable": true,
        "title": "tauri-mobile",
        "width": 800,
        "height": 600
      }
    ]
  }
}
```

Install iOS codegen dependencies:
```bash
$ brew install cocoapods
```

Install core Cargo dependecies:
```bash
$ cargo add tauri@2.0.0-alpha.<VERSION>
$ cargo add tauri-build@2.0.0-alpha.<VERSION> --build
```
If you'll get the following error:
```bash
➜ cargo add tauri@2.0.0-alpha.10

    Updating crates.io index

      Adding tauri v2.0.0-alpha.10 to dependencies.

error: unrecognized feature for crate tauri: shell-open
```

all you do is
`src-tauri/Cargo.toml`
```diff
[package]

name = "tauri-mobile"

version = "0.0.0"

description = "A Tauri App"

authors = ["you"]

license = ""

repository = ""

edition = "2021"

  

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

  

[build-dependencies]

tauri-build = { version = "1.4", features = [] }

  

[dependencies]

- tauri = { version = "1.4", features = ["shell-open"] }

serde = { version = "1.0", features = ["derive"] }

serde_json = "1.0"

  

[features]

# this feature is used for production builds or when `devPath` points to the filesystem

# DO NOT REMOVE!!

- custom-protocol = ["tauri/custom-protocol"]
```
after upgrading dependencies put this line back
`custom-protocol = ["tauri/custom-protocol"]`

Include embedded library
`src-tauri/Cargo.toml`
```rust
[lib]
crate-type = ["staticlib", "cdylib", "rlib"]
```
Then you set up rest of the project files.
Library
`src-tauri/src/lib.rs`
```rust
use tauri::App;

#[cfg(mobile)]
mod mobile;
#[cfg(mobile)]
pub use mobile::*;

pub type SetupHook = Box<dyn FnOnce(&mut App) -> Result<(), Box<dyn std::error::Error>> + Send>;

#[derive(Default)]
pub struct AppBuilder {
  setup: Option<SetupHook>,
}

impl AppBuilder {
  pub fn new() -> Self {
    Self::default()
  }

  #[must_use]
  pub fn setup<F>(mut self, setup: F) -> Self
  where
    F: FnOnce(&mut App) -> Result<(), Box<dyn std::error::Error>> + Send + 'static,
  {
    self.setup.replace(Box::new(setup));
    self
  }

  pub fn run(self) {
    let setup = self.setup;
    tauri::Builder::default()
      .setup(move |app| {
        if let Some(setup) = setup {
          (setup)(app)?;
        }
        Ok(())
      })
      .run(tauri::generate_context!())
      .expect("error while running tauri application");
  }
}
```

Entry point
`src-tauri/src/mobile.rs`
```rust
#[tauri::mobile_entry_point]
fn main() {
  super::AppBuilder::new().run();
}
```

Main
`src-tauri/src/main.rs`
```rust
#![cfg_attr(
  all(not(debug_assertions), target_os = "windows"),
  windows_subsystem = "windows"
)]

pub fn main() {
  app::AppBuilder::new().run();
}
```

That's all you need!
Now run `ios dev` and test your app with an emulator or connected iOS device.