merge in docs from next branch

Signed-off-by: Lorenzo Lewis <lorenzo_lewis@icloud.com>
This commit is contained in:
Lorenzo Lewis
2023-08-03 19:15:48 +01:00
parent cefd8705dd
commit 23e66d57be
4 changed files with 218 additions and 3 deletions

View File

@@ -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)

View File

@@ -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)
</Stub>

View File

@@ -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<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");
}
}
```
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!");
}
}
```

View File

@@ -4,4 +4,8 @@ title: Develop
import Stub from '@components/Stub.astro';
<Stub />
<Stub>
- `pnpm tauri [android|ios] dev [--open]`
</Stub>