# React Native (iOS)

Forkbook can show your stories on an iPhone, in an Expo app. The Forkbook UI on the device uses SwiftUI through Expo UI.

## Packages

Your app must have these packages. Forkbook does not install them, because each one has native code or is the platform:

- `react`
- `react-native`
- `@expo/ui`
- `expo-symbols`
- `react-native-safe-area-context`

Add Forkbook:

```sh
bun add -d @aussieljk/forkbook
bunx forkbook init --type react_native
```

## The Metro configuration

```js
// metro.config.js
const path = require('node:path');
const { getDefaultConfig } = require('expo/metro-config');
const { withForkbook } = require('@aussieljk/forkbook/react-native/metro/withForkbook');

const config = getDefaultConfig(__dirname);

module.exports = withForkbook(config, {
  enabled: process.env.FORKBOOK_ENABLED === 'true',
  configPath: path.resolve(__dirname, '.rnforkbook'),
});
```

When `enabled` is `false`, every Forkbook import resolves to an empty module. A release build then has no Forkbook code.

### Options of withForkbook

| Option | Default | What it does |
| --- | --- | --- |
| `enabled` | `true` | Puts Forkbook in the bundle. |
| `configPath` | `./.rnforkbook` | The folder with `main.ts`, `preview.tsx` and `index.tsx`. |
| `liteMode` | `false` | Uses a smaller UI with no addons panel. |
| `docTools` | `true` | Adds the tools that make the args table. |
| `useJs` | `false` | Writes `forkbook.requires.js` in place of `forkbook.requires.ts`. |
| `websockets` | none | Starts a channel server, so a web Forkbook or a test can control the device. Use `'auto'` or `{ host, port, secured }`. |

## The configuration folder

```ts
// .rnforkbook/main.ts
import type { ForkbookConfig } from '@aussieljk/forkbook/react-native';

const main: ForkbookConfig = {
  stories: ['../src/**/*.stories.?(ts|tsx)'],
  deviceAddons: [
    '@aussieljk/forkbook/addon-ondevice-controls',
    '@aussieljk/forkbook/addon-ondevice-actions',
  ],
};

export default main;
```

```tsx
// .rnforkbook/index.tsx
import AsyncStorage from '@react-native-async-storage/async-storage';

import { view } from './forkbook.requires';

const ForkbookUIRoot = view.getForkbookUI({
  storage: { getItem: AsyncStorage.getItem, setItem: AsyncStorage.setItem },
});

export default ForkbookUIRoot;
```

The `storage` option keeps the last story between launches. It is optional.

## The story list

`forkbook.requires.ts` lists your stories. Metro writes it when it starts. To write it yourself, run:

```sh
bunx forkbook rn-stories --config-path .rnforkbook
```

Commit this file. Do not format it with a formatter, because Metro writes it again in its own format.

## Write a story

```tsx
import type { Meta, StoryObj } from '@aussieljk/forkbook/react-native';

import { Button } from './button';

const meta = { component: Button } satisfies Meta<typeof Button>;
export default meta;

export const Primary: StoryObj<typeof meta> = {
  args: { title: 'Save' },
};
```

## Show Forkbook in the app

Show `ForkbookUIRoot` in place of your app when `FORKBOOK_ENABLED` is set. For example, in `index.ts`, export the Forkbook root when the variable is `true`.

## Tests

Use `composeStories` from `@aussieljk/forkbook/react` to render the stories in Vitest with `@testing-library/react`.

## Environment variables

| Variable | What it does |
| --- | --- |
| `FORKBOOK_ENABLED` | Your scripts set it to show Forkbook. |
| `FORKBOOK_SERVER` | Set to `false` to not start the channel server. |
| `FORKBOOK_WS_HOST`, `FORKBOOK_WS_PORT`, `FORKBOOK_WS_SECURED` | The address of the channel server. |

## Not supported

- Android.
- React Native Web.
- Apps without Expo (`@expo/ui` needs Expo).
