A React Native mobile application that provides a native wrapper for the Republik.ch website with enhanced native capabilities.
The Republik App is built with Expo SDK 55 and React Native 0.83, serving as a WebView wrapper for the Republik.ch website. The app enables browsing of Republik content while providing native mobile features like background audio playback, push notifications, and deep linking.
- WebView Container (
components/Web.tsx): Main component that renders the Republik website and handles communication - Audio Player (
components/AudioPlayer/ExpoAudioPlayer.tsx): Headless React component usingexpo-audiowith native lock screen controls - Global State Management (
lib/GlobalState.tsx): Centralized state management for app-wide data - Services: Background services for deep linking, push notifications, and app state
The app uses a PostMessage API to communicate between the native React Native layer and the web content:
// Native → Web: Sending messages to the webview
dispatch({
type: "postMessage",
content: {
type: "push-route",
url: "/article/123"
}
});
// Web → Native: Receiving messages from webview
const onMessage = (e: WebViewMessageEvent) => {
const message = JSON.parse(e.nativeEvent.data);
switch (message.type) {
case "play-audio":
// Handle audio playback
break;
case "share":
// Handle native sharing
break;
// ... other message types
}
};- Node.js ≥ 18 (LTS version recommended)
- pnpm (enable via Corepack:
corepack enable) - iOS Simulator (macOS) or Android Emulator; requires Xcode 16+ and Android Studio
- Install dependencies:
pnpm install- Start the development server:
pnpm start
# or
pnpm exec expo startpnpm ios
# or
pnpm exec expo run:iospnpm android
# or
pnpm exec expo run:androidFor testing on physical devices, a development build needs to be created.
Prerequisites:
- EAS CLI installed:
pnpm add -g @expo/eas-cli - Expo account (sign up at expo.dev)
- EAS CLI logged in:
eas login
Create development builds:
For iOS:
eas build --profile development --platform iosFor Android:
eas build --profile development --platform androidThe builds will be available in your Expo dashboard for download and installation.
Copy .env.example to .env and fill in the required values:
EXPO_PUBLIC_ENV: Environment name (developmentorproduction)EXPO_PUBLIC_FRONTEND_BASE_URL: Base URL for the WebView (defaults tohttps://www.republik.ch)SENTRY_AUTH_TOKEN: Sentry auth token for source map uploads at build time (not bundled into the app)
app.json: Expo app configurationeas.json: EAS Build configurationpackage.json: Dependencies and scriptspnpm-lock.yaml: Locked dependency versions (usepnpm install, not npm)
ExpoAudioPlayer is a headless React component mounted at the app root alongside the WebView. It uses expo-audio's createAudioPlayer API and communicates with the web layer entirely through the PostMessage bridge.
Lazy initialization: the track is set up via a SETUP_TRACK event before the user hits play. The audio session is started only when PLAY is received, reducing unnecessary resource usage.
- Background audio playback
- Native lock screen controls
- Playback speed control
- Forward / backward seek
- Progress synchronization with the web UI at 500 ms intervals while playing
- Android hardware back button collapses the expanded player UI
- Automatic state sync when the app returns to foreground
The app persists the WebView's current URL to MMKV storage so users return to their last-viewed page on next launch. Three complementary strategies ensure the URL is saved reliably:
- Navigation events: The web frontend sends a
routeChangepostMessage on every navigation. On iOS,onNavigationStateChangealso fires forpushStatenavigations. Both paths write to MMKV immediately. - Periodic sync: A 10-second interval reads
window.location.hreffrom the WebView and persists it if it differs from the stored value — a safety net in case arouteChangemessage is dropped. - Background flush: When the app transitions to background or inactive, a synchronous MMKV write is forced as a last-chance flush before the OS suspends the process.
Android note:
onNavigationStateChangedoes not fire forhistory.pushState()calls. SPA navigation on Android relies entirely onrouteChangemessages from the web frontend.
The app handles incoming republik.ch URLs and routes them directly into the WebView:
- iOS: Associated domains configured for
republik.chandwww.republik.ch - Android: Intent filters for HTTPS scheme on both domains
Push notifications are handled via Expo Notifications. Tapping a notification navigates the WebView to the notification's target URL.
- iOS: APNs configuration
- Android: FCM via
google-services.json
The app uses Expo Application Services (EAS) for building and deploying to app stores.
The app has three build profiles configured in eas.json:
- Development: Development client builds for testing
- Preview: Internal distribution builds for testing
- Production: App store ready builds with auto-increment
- Install EAS CLI:
pnpm add -g @expo/eas-cli- Login to your Expo account:
eas logineas build --profile development --platform ios
eas build --profile development --platform androideas build --profile preview --platform ios
eas build --profile preview --platform androideas build --profile production --platform ios
eas build --profile production --platform androideas submit --platform ios --profile productioneas submit --platform android --profile productionDownload the «Distribution APK» file from Google Play Console and upload it to the APK hosting location.
Make sure to update the APK download link at republik.ch/app/apk/latest so that it points to the newly uploaded file.