Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/assets/app-icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 12 additions & 3 deletions .github/workflows/eas-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ env:
EXPO_PUBLIC_OPENAPI_URL: ${{ vars.OPENAPI_URL }}

on:
push:
branches: [main]
workflow_dispatch:
inputs:
profile:
description: EAS build profile
required: true
default: production
type: choice
options:
- development
- staging
- production

jobs:
build:
Expand All @@ -32,4 +41,4 @@ jobs:
run: pnpm install

- name: πŸš€ Build app
run: eas build --non-interactive
run: eas build --profile ${{ github.event.inputs.profile }} --non-interactive
33 changes: 33 additions & 0 deletions .github/workflows/eas-development-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
name: πŸ”¨ EAS Development build

env:
EXPO_PUBLIC_API_URL: ${{ vars.API_URL }}
EXPO_PUBLIC_AUTH_URL: ${{ vars.AUTH_URL }}
EXPO_PUBLIC_OPENAPI_URL: ${{ vars.OPENAPI_URL }}

on: workflow_dispatch

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: πŸ— Setup repo
uses: actions/checkout@v5

- name: πŸ— Setup Node
uses: actions/setup-node@v5
with:
node-version: 'lts/*'
cache: 'pnpm'

- name: πŸ— Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}

- name: πŸ“¦ Install dependencies
run: pnpm install

- name: πŸš€ Build development client
run: eas build --profile development --non-interactive
17 changes: 13 additions & 4 deletions .github/workflows/eas-update.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,17 @@ env:
EXPO_PUBLIC_OPENAPI_URL: ${{ vars.OPENAPI_URL }}

on:
push:
branches: [main]
workflow_dispatch:
inputs:
channel:
description: EAS Update channel (matches build profile)
required: true
default: production
type: choice
options:
- development
- staging
- production

jobs:
update:
Expand All @@ -31,5 +40,5 @@ jobs:
- name: πŸ“¦ Install dependencies
run: pnpm install

- name: πŸš€ Create update
run: eas update --auto --non-interactive
- name: πŸš€ Publish update
run: eas update --branch ${{ github.event.inputs.channel }} --non-interactive
43 changes: 43 additions & 0 deletions .github/workflows/prepare-production.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: πŸš€ Release Production

env:
EXPO_PUBLIC_API_URL: ${{ vars.API_URL }}
EXPO_PUBLIC_AUTH_URL: ${{ vars.AUTH_URL }}
EXPO_PUBLIC_OPENAPI_URL: ${{ vars.OPENAPI_URL }}

on: workflow_dispatch

jobs:
prepare-production:
runs-on: ubuntu-latest
steps:
- name: πŸ— Setup repo
uses: actions/checkout@v5

- name: πŸ— Setup Node
uses: actions/setup-node@v5
with:
node-version: 'lts/*'
cache: 'pnpm'

- name: πŸ— Setup EAS
uses: expo/expo-github-action@v8
with:
eas-version: latest
token: ${{ secrets.EXPO_TOKEN }}

- name: πŸ“¦ Install dependencies
run: pnpm install

- name: πŸš€ Build production
run: eas build --profile production --platform all --non-interactive

- name: Setup App Store Connect API key
run: |
echo "$APP_STORE_CONNECT_API_KEY" | base64 -d > "$ASC_API_KEY_PATH"
env:
APP_STORE_CONNECT_API_KEY: ${{ secrets.APP_STORE_CONNECT_API_KEY }}
ASC_API_KEY_PATH: AuthKey_${{ vars.ASC_API_KEY_ID }}.p8

- name: πŸš€ Submit on stores
run: eas submit --profile production --platform all --latest --non-interactive
6 changes: 4 additions & 2 deletions .prettierrc.cjs
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
// Prettier configuration. Please avoid changing the current configuration.
// But if you do so, please run the `npm run pretty` command.
// But if you do so, please run the `pnpm pretty` command.
/** @type {import("prettier").Options} */
const config = {
plugins: ['prettier-plugin-tailwindcss'],
endOfLine: 'lf',
semi: true,
singleQuote: true,
tabWidth: 2,
trailingComma: 'es5',
arrowParens: 'always',
tailwindStylesheet: './src/app.css',
tailwindFunctions: ['cn', 'tv'],
};

// eslint-disable-next-line no-undef
module.exports = config;
11 changes: 6 additions & 5 deletions .rnstorybook/storybook.requires.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,23 @@ const normalizedStories = [
declare global {
var view: View;
var STORIES: typeof normalizedStories;
var STORYBOOK_WEBSOCKET: { host: string; port: number } | undefined;
}

const annotations = [require('@storybook/react-native/preview')];

global.STORIES = normalizedStories;
globalThis.STORIES = normalizedStories;

// @ts-ignore
module?.hot?.accept?.();

if (!global.view) {
global.view = start({
if (!globalThis.view) {
globalThis.view = start({
annotations,
storyEntries: normalizedStories,
});
} else {
updateView(global.view, annotations, normalizedStories);
updateView(globalThis.view, annotations, normalizedStories);
}

export const view: View = global.view;
export const view: View = globalThis.view;
128 changes: 98 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,22 @@
πŸš€ Start UI <small>[native]</small> is an opinionated native starter repository created & maintained by the [BearStudio Team](https://www.bearstudio.fr/team) and other contributors.
It represents our team's up-to-date stack that we use when creating native apps for our clients.


# Technologies

<div align="center" style="margin: 0 0 16px 0"><img src=".github/assets/tech-logos.png" alt="Technologies logos of the starter" /></div>

[βš™οΈ Node.js](https://nodejs.org), [🟦 TypeScript](https://www.typescriptlang.org/), [βš›οΈ React](https://react.dev/), [πŸ“± React Native](https://reactnative.dev/), [πŸš€ Expo](https://docs.expo.dev/), [πŸ” Better Auth](https://www.better-auth.com/), [🌿 Ficus UI](https://ficus-ui.com/), [🌴 Tanstack Form](https://tanstack.com/form/), [🌴 Tanstack Query](https://tanstack.com/query/), [πŸ‘‹ Hey API](https://heyapi.dev/)
[βš™οΈ Node.js](https://nodejs.org), [🟦 TypeScript](https://www.typescriptlang.org/), [βš›οΈ React](https://react.dev/), [πŸ“± React Native](https://reactnative.dev/), [πŸš€ Expo](https://docs.expo.dev/), [πŸ” Better Auth](https://www.better-auth.com/), [πŸ–ŒοΈ Uniwind](https://uniwind.dev), [🧱 React Native Reusables](https://reactnativereusables.com), [🌴 Tanstack Form](https://tanstack.com/form/), [🌴 Tanstack Query](https://tanstack.com/query/), [πŸ‘‹ Hey API](https://heyapi.dev/)

# Requirements

* [Node.js](https://nodejs.org) >= 22
* [pnpm](https://pnpm.io/)
- [Node.js](https://nodejs.org) >= 22
- [pnpm](https://pnpm.io/)

# Getting Started

```bash
pnpm create start-ui -t native myApp
```

That will scaffold a new folder with the latest version of πŸš€ Start UI <small>[native]</small> πŸŽ‰
That will scaffold a new folder with the latest version of πŸš€ Start UI [native] πŸŽ‰

# Installation

Expand All @@ -33,7 +30,6 @@ pnpm install # Install dependencies

## Environment variables


> [!TIP]
> Using Expo Go, local development urls should not be `localhost`, use public IP instead

Expand All @@ -54,6 +50,7 @@ pnpm gen:api
A new folder `/src/lib/hey-api/generated` will be created with stuff like to combine use Tanstack Query to fetch data, based on env variables.

For example

```ts
import { api } from '@/lib/hey-api/api';

Expand All @@ -62,22 +59,43 @@ useQuery(api.bookGetByIdOptions({ path: { id: props.bookId } }));

# Run


### Expo Go

```bash
pnpm dev
```

### Local build
### Local builds

App name and bundle ID come from `app.config.ts`, evaluated at **prebuild** time. Each script runs `prebuild --clean` with the appropriate `APP_ENV`, then builds.


| Script | App name | Build type | Need |
| --------------------------------------------------- | --------------------------- | ---------- | ----------------------------------------- |
| `dev:build:ios` / `dev:build:android` | Start UI [native] [Dev] | Dev/Debug | Local development, replacement of Expo Go |
| `build:staging:ios` / `build:staging:android` | Start UI [native] [Staging] | Release | Install staging release version locally |
| `build:production:ios` / `build:production:android` | Start UI [native] | Release | Install production version locally |


```bash
pnpm prebuild # To generate local builds
# Development (debug build, dev client)
pnpm dev:build:ios
pnpm dev:build:android

# Staging (release build, internal testing)
pnpm build:staging:ios
pnpm build:staging:android

pnpm dev:ios # To run with local ios build
pnpm dev:android # To run with local android build
# Production (release build, store-like)
pnpm build:production:ios
pnpm build:production:android
```

> [!TIP]
> If the app name is wrong, the native project was generated with the wrong `APP_ENV`. Re-run the same build script β€” it includes `--clean` to regenerate the native project.

<h1 align="center"><img src=".github/assets/app-icons.png" alt="App icons render in ios" /></h1>

### Devtools

You can use @dev-plugins pressing `Shift + m` in your Expo terminal.
Expand All @@ -87,13 +105,12 @@ You can use @dev-plugins pressing `Shift + m` in your Expo terminal.
Storybook is managed as a specific mode of the app that is launch apart in port 8083

```bash
pnpm storybook # To run app in storybook mode
pnpm dev:storybook # To run app in storybook mode
```

> [!TIP]
> You can open Storybook in another tab and switch between the app and Storybook by pressing `i` or `a` in each terminal.


## Generate custom icons components from svg files

Put the custom svg files into the `app/components/icons/svg-sources` folder and then run the following command:
Expand All @@ -108,22 +125,73 @@ If you want to use the same set of custom duotone icons that Start UI is already
> [!WARNING]
> All svg icons should be svg files prefixed by `icon-` (example: `icon-externel-link`) with **square size** and **filled with `#000` color** (will be replaced by `currentColor`).

# GitHub repository configuration

The GitHub Actions workflows require the following configuration.

## Variables

Add at **Settings β†’ Secrets and variables β†’ Actions β†’ Variables**:


| Variable | Description | Used by |
| ---------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------- |
| `API_URL` | Base URL of your API server | EAS builds, preview, update, code quality, prepare-production |
| `AUTH_URL` | Better Auth URL (e.g. `https://your-api.com/api/auth`) | EAS builds, preview, update, code quality, prepare-production |
| `OPENAPI_URL` | OpenAPI schema URL (e.g. `https://your-api.com/openapi/app/schema`) | EAS builds, preview, update, code quality, prepare-production |
| `ASC_API_KEY_ID` | App Store Connect API Key ID (e.g. `H3KZ2V5L32`) for the Release Production workflow | prepare-production |


## Secrets

Add at **Settings β†’ Secrets and variables β†’ Actions β†’ Secrets**:


| Secret | Description | Used by |
| --------------------------- | ------------------------------------------------------------------------ | ----------------------------------------------------------------- |
| `EXPO_TOKEN` | Expo access token for EAS CLI | EAS build, development build, update, preview, prepare-production |
| `APP_STORE_CONNECT_API_KEY` | Base64-encoded App Store Connect API key (.p8 file) for store submission | prepare-production |


Create an Expo token at [expo.dev/accounts/{account}/settings/access-tokens](https://expo.dev/accounts/_/settings/access-tokens).

For `APP_STORE_CONNECT_API_KEY`: export your App Store Connect API key as base64 (`base64 -i AuthKey_XXX.p8`) and store the result as the secret. The prepare-production workflow decodes it at runtime for `eas submit`.

For the **Release Production** workflow, also configure `eas.json` submit.production with your App Store Connect credentials (`ascAppId`, `ascApiKeyId`, `ascApiKeyIssuerId`, `ascApiKeyPath`), and set the `ASC_API_KEY_ID` variable to match your key ID (used in the key filename).

## Workflows


| Workflow | Trigger | Description |
| ------------------------- | ------------ | --------------------------------------------------------------------------------------- |
| **EAS build** | Manual | Build app β€” select profile (development, staging, production) in the GitHub UI |
| **EAS Development build** | Manual | Build development client (internal) |
| **EAS Update** | Manual | Publish OTA update β€” select channel (development, staging, production) in the GitHub UI |
| **EAS Preview** | Pull request | Publish preview update on PR branch |
| **Release Production** | Manual | Build production, then submit to App Store and Play Store |


### EAS build profiles


| Profile | Purpose |
| --------------- | ------------------------------------------------------------------------ |
| **development** | Dev client for local development (internal distribution) |
| **staging** | Internal distribution for testers (never sent to stores) |
| **production** | Store build (auto-increments, submitted via Release Production workflow) |


# EAS Preview

To be able to use previews on PR, you have to setup your project with EAS

1. Setup Expo access token
* Create it: https://expo.dev/accounts/{account}/settings/access-tokens
* Add it as GitHub repository secrets: https://github.com/xxx/xxx/settings/secrets/actions
2. Add GitHub repository variables: https://github.com/xxx/xxx/settings/variables/actions
* `API_URL`
* `AUTH_URL`
* `OPENAPI_URL`
3. Setup Expo project: https://expo.dev/
* Create your project
* Get project's id
* Set as `EXPO_PROJECT_ID` in `app.config.ts`
4. Setup eas
* `eas login`
* `eas init --id {projectid}`
* `eas update:configure`
1. Configure [GitHub variables and secrets](#github-repository-configuration) (see above)
2. Setup Expo project: [https://expo.dev/](https://expo.dev/)
- Create your project
- Get project's id
- Set as `EAS_PROJECT_ID` in `app.config.ts`
3. Setup eas
- `eas login`
- `eas init --id {projectid}`
- `eas update:configure`

Loading
Loading