diff --git a/src/content/docs/2/guide/build-distribute/index.mdx b/src/content/docs/2/guide/build-distribute/index.mdx index e82e6e2db..e29a647ff 100644 --- a/src/content/docs/2/guide/build-distribute/index.mdx +++ b/src/content/docs/2/guide/build-distribute/index.mdx @@ -10,7 +10,7 @@ import Stub from '@components/Stub.astro'; - Linux - macOS - Windows - - Android + - Android `pnpm tauri [ios|android] build` - iOS - Updater (link to recipe) diff --git a/src/content/docs/2/guide/create.mdx b/src/content/docs/2/guide/create/index.mdx similarity index 97% rename from src/content/docs/2/guide/create.mdx rename to src/content/docs/2/guide/create/index.mdx index 02f05cd05..9c564cd02 100644 --- a/src/content/docs/2/guide/create.mdx +++ b/src/content/docs/2/guide/create/index.mdx @@ -9,7 +9,7 @@ import Stub from '@components/Stub.astro'; - Create a new app using `create-tauri-app` - Manually create the app using the Tauri CLI -- Add mobile to an existing project +- Add mobile to an existing project (revise what's here [integrate mobile](./mobile)) - Frontend framework-specific guides (right now limiting to React, Angular, Vue, Svelte, and Solid) diff --git a/src/content/docs/2/guide/create/mobile.mdx b/src/content/docs/2/guide/create/mobile.mdx new file mode 100644 index 000000000..6bb80ec91 --- /dev/null +++ b/src/content/docs/2/guide/create/mobile.mdx @@ -0,0 +1,211 @@ +--- +title: Integrate +--- + +If you have a desktop Tauri project and want to target mobile platforms using the same code, you must configure your Rust crate. + +## Change your crate type + +The Rust crate must output a library that will be embedded in the Android and iOS packages. Add the following section to your `Cargo.toml` file: + +```toml title=src-tauri/Cargo.toml +[lib] +crate-type = ["staticlib", "cdylib", "rlib"] +``` + +## Create the library source code + +The default entry point for the Rust library is the `lib.rs` file. +Let's write a `tauri::Builder` wrapper that will be reused by both the desktop and mobile targets. + +```rust title=src-tauri/src/lib.rs +use tauri::App; + +#[cfg(mobile)] +mod mobile; +#[cfg(mobile)] +pub use mobile::*; + +pub type SetupHook = Box Result<(), Box> + Send>; + +#[derive(Default)] +pub struct AppBuilder { + setup: Option, +} + +impl AppBuilder { + pub fn new() -> Self { + Self::default() + } + + #[must_use] + pub fn setup(mut self, setup: F) -> Self + where + F: FnOnce(&mut App) -> Result<(), Box> + 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"); + } +} +``` + +In this example, the `AppBuilder::run` function is where you write all the shared logic. Custom mobile/desktop code can be defined in the AppBuilder methods, such as the setup method. + +Now let's create the mobile module that will have the entry point for iOS and Android and consume the shared AppBuilder logic: + +```rust title=src-tauri/src/mobile.rs +#[tauri::mobile_entry_point] +fn main() { + super::AppBuilder::new().run(); +} +``` + +The default Tauri project has the binary source code under `src-tauri/src/main.rs`. Let's change it to use the AppBuilder struct: + +```rust title=src-tauri/src/main.rs +#![cfg_attr( + all(not(debug_assertions), target_os = "windows"), + windows_subsystem = "windows" +)] + +pub fn main() { + app::AppBuilder::new().run(); +} + +``` + +## Frontend Configuration + +To develop mobile Tauri applications, your frontend must serve the assets listening on your public network address. +The network address can be found using the `internal-ip` NPM: + +```shell +npm install --save-dev internal-ip +``` + +```shell +yarn add -D internal-ip +``` + +```shell +pnpm add -D internal-ip +``` + +Then you need to configure your framework to use the internal IP. + +### Vite + +For Vite, you need to change your configuration to be defined using the `defineConfig` helper with an async closure. +Then, resolve the internal IP address and set it to the [server object][vite server]. + +```javascript title=vite.config.js +import { defineConfig } from 'vite'; +import { internalIpV4 } from 'internal-ip'; + +// https://vitejs.dev/config/ +export default defineConfig(async () => { + const host = await internalIpV4(); + + /** @type {import('vite').UserConfig} */ + const config = { + server: { + host: '0.0.0.0', // listen on all addresses + port: 5173, + strictPort: true, + hmr: { + protocol: 'ws', + host, + port: 5183, + }, + }, + }; + + return config; +}); +``` + +### Next.js + +For Next.js, you need to configure the [assetPrefix] to use the internal IP so the server properly resolves your assets. + +```typescript title=next.config.js +const isProd = process.env.NODE_ENV === 'production'; +module.exports = async (phase, { defaultConfig }) => { + let internalHost = null; + if (!isProd) { + const { internalIpV4 } = await import('internal-ip'); + internalHost = await internalIpV4(); + } + /** + * @type {import('next').NextConfig} + */ + const nextConfig = { + reactStrictMode: true, + swcMinify: true, + // Note: This experimental feature is required to use NextJS Image in SSG mode. + // See https://nextjs.org/docs/messages/export-image-api for different workarounds. + images: { + unoptimized: true, + }, + assetPrefix: isProd ? null : `http://${internalHost}:3000`, + }; + return nextConfig; +}; +``` + +Currently, there is no configuration option to configure Next.js to use the internal IP address, only the CLI allows changing it. +So you need to append `--hostname $HOST` to the [beforeDevCommand]. + +### Webpack + +Webpack has a built-in option to use the local IP address as the host for the development server: + +```typescript title=webpack.config.js +export default { + devServer: { + host: 'local-ipv4', + }, +}; +``` + +[vite server]: https://vitejs.dev/config/server-options.html +[assetprefix]: https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix +[beforedevcommand]: ../../api/config.md#buildconfig.beforedevcommand +[tauri releases on github]: https://github.com/tauri-apps/tauri/releases + +## Conditional compilation + +The `#[cfg(desktop)]` and `#[cfg(mobile)]` conditional checks can be used to conditionally compile code for each target. + +```rust title=lib.rs +#[cfg(mobile)] +fn do_something() { + println!("Hello from Mobile!"); +} + +#[cfg(desktop)] +fn do_something() { + println!("Hello from Desktop!"); +} + +fn run() { + if cfg!(mobile) { + println!("Hello from Mobile!"); + } else { + println!("Hello from Desktop!"); + } +} +``` diff --git a/src/content/docs/2/guide/develop.mdx b/src/content/docs/2/guide/develop.mdx index 85e0efd45..2b82a7c7f 100644 --- a/src/content/docs/2/guide/develop.mdx +++ b/src/content/docs/2/guide/develop.mdx @@ -4,4 +4,8 @@ title: Develop import Stub from '@components/Stub.astro'; - + + +- `pnpm tauri [android|ios] dev [--open]` + +