Skip to main content

Overview

WPM Typing Tutor extends to mobile platforms through React Native with Expo Go, providing a native mobile experience for Android and iOS devices. The mobile app wraps the Unity WebGL build within a WebView, enabling cross-platform gameplay with native mobile integration.
Framework: React Native with Expo SDKDeployment: Expo Go appPlatforms: Android (available), iOS (coming soon)Unity Integration: WebView wrapper

Architecture Overview

The mobile app uses a hybrid architecture:
1

React Native Shell

A React Native application provides:
  • Native mobile app structure
  • Navigation and UI framework
  • Platform-specific capabilities
  • App lifecycle management
2

WebView Container

The game runs inside a WebView component:
  • Loads Unity WebGL build from hosted URL
  • Provides JavaScript bridge for native communication
  • Handles touch input and gestures
  • Manages viewport and orientation
3

Unity WebGL Game

The same Unity build used for web:
  • No modifications needed to Unity code
  • Touch events work automatically
  • Responsive to viewport changes
  • Same gameplay experience

Expo Configuration

Project Setup

The app is configured using Expo’s managed workflow:
{
  "expo": {
    "name": "WPM Typing Tutor",
    "slug": "wpm-typing-tutor",
    "version": "1.0.0",
    "orientation": "landscape",
    "icon": "./assets/icon.png",
    "userInterfaceStyle": "dark",
    "splash": {
      "image": "./assets/splash.png",
      "resizeMode": "contain",
      "backgroundColor": "#0d0d0d"
    },
    "platforms": ["ios", "android"],
    "android": {
      "package": "com.wpm.typingtutor",
      "adaptiveIcon": {
        "foregroundImage": "./assets/adaptive-icon.png",
        "backgroundColor": "#0d0d0d"
      },
      "permissions": []
    },
    "ios": {
      "bundleIdentifier": "com.wpm.typingtutor",
      "supportsTablet": true
    }
  }
}
The mobile app uses landscape orientation by default to match the Unity WebGL game’s design and provide the best typing experience.

WebView Integration

Basic Implementation

The core of the mobile app is a WebView component that loads the Unity game:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { WebView } from 'react-native-webview';
import { StatusBar } from 'expo-status-bar';

export default function App() {
  return (
    <View style={styles.container}>
      <StatusBar hidden />
      <WebView
        source={{ uri: 'https://your-domain.com' }}
        style={styles.webview}
        javaScriptEnabled={true}
        domStorageEnabled={true}
        startInLoadingState={true}
        scalesPageToFit={true}
        allowsFullscreenVideo={false}
      />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#0d0d0d',
  },
  webview: {
    flex: 1,
  },
});

WebView Configuration Options

Critical WebView props for Unity WebGL:
javaScriptEnabled={true}
Required for Unity WebGL to run. Unity uses JavaScript extensively for WebGL initialization and game logic.

Touch Input Handling

Unity Touch Events

Unity WebGL automatically handles touch input:
Unity Touch Support
// Unity's Input system maps touch to mouse events:
// - Touch = Mouse click
// - Swipe = Mouse drag
// - Pinch = Not supported (not needed for typing game)

// In Unity C# code, use standard input:
if (Input.GetMouseButtonDown(0)) {
    // Handles both mouse clicks and touch
}

// Or use Unity's Touch API directly:
if (Input.touchCount > 0) {
    Touch touch = Input.GetTouch(0);
    if (touch.phase == TouchPhase.Began) {
        // Handle touch
    }
}

Keyboard Input on Mobile

The typing game requires a physical or virtual keyboard. On mobile devices:
  • Android: Virtual keyboard automatically appears
  • iOS: Virtual keyboard can be triggered programmatically
  • Best experience: External Bluetooth keyboard
import React, { useEffect } from 'react';
import { Keyboard } from 'react-native';
import { WebView } from 'react-native-webview';

export default function GameView() {
  useEffect(() => {
    // Keep keyboard visible during gameplay
    const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
      console.log('Keyboard shown');
    });
    
    const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
      console.log('Keyboard hidden');
    });

    return () => {
      showSubscription.remove();
      hideSubscription.remove();
    };
  }, []);

  return (
    <WebView
      source={{ uri: 'https://your-domain.com' }}
      // ... other props
    />
  );
}

JavaScript Bridge

Communicate between React Native and Unity using the WebView JavaScript bridge:
import React, { useRef } from 'react';
import { View, Button } from 'react-native';
import { WebView } from 'react-native-webview';

export default function App() {
  const webViewRef = useRef(null);

  // Send message to Unity
  const sendToUnity = (message) => {
    webViewRef.current?.injectJavaScript(`
      if (window.unityInstance) {
        window.unityInstance.SendMessage('GameManager', 'ReceiveMessage', '${message}');
      }
      true;
    `);
  };

  return (
    <View style={{ flex: 1 }}>
      <WebView
        ref={webViewRef}
        source={{ uri: 'https://your-domain.com' }}
        onMessage={(event) => {
          // Receive messages from Unity
          const data = JSON.parse(event.nativeEvent.data);
          console.log('Message from Unity:', data);
        }}
      />
      <Button 
        title="Start Game" 
        onPress={() => sendToUnity('START')} 
      />
    </View>
  );
}

Development Workflow

Local Development with Expo

1

Install Expo CLI

npm install -g expo-cli
2

Create Expo Project

npx create-expo-app wpm-typing-tutor-mobile
cd wpm-typing-tutor-mobile
3

Install Dependencies

npm install react-native-webview
npx expo install expo-splash-screen expo-status-bar
4

Configure App

Edit app.json with your app configuration (see above examples).
5

Start Development Server

npx expo start
This opens the Expo Developer Tools in your browser.
6

Test on Device

Android:
  • Install Expo Go from Play Store
  • Scan QR code from Expo Dev Tools
iOS:
  • Install Expo Go from App Store
  • Scan QR code with Camera app
During development, you can point the WebView to localhost if running the web server locally, or use a tunneling service like ngrok for remote testing.

Testing with Local Unity Build

Development Configuration
// Point to local development server
const DEV_MODE = __DEV__; // Expo provides this automatically

const GAME_URL = DEV_MODE 
  ? 'http://192.168.1.100:3000' // Your local IP
  : 'https://your-production-domain.com';

export default function App() {
  return (
    <WebView
      source={{ uri: GAME_URL }}
      // ... other props
    />
  );
}

Platform-Specific Features

Android Configuration

app.json
{
  "android": {
    "permissions": [
      // Add only required permissions
      // WPM Typing Tutor doesn't need any special permissions
    ]
  }
}

iOS Configuration (Coming Soon)

iOS support is planned but not yet available. The configuration is prepared for future release.
app.json - iOS Settings
{
  "ios": {
    "bundleIdentifier": "com.wpm.typingtutor",
    "buildNumber": "1.0.0",
    "supportsTablet": true,
    "infoPlist": {
      "UIRequiresFullScreen": true,
      "UIStatusBarHidden": true
    }
  }
}

Performance Optimization

WebView Performance

Hardware Acceleration

Enable hardware acceleration in WebView for better Unity performance:
<WebView
  androidLayerType="hardware"
  // ... other props
/>

Cache Management

Enable caching to speed up subsequent loads:
<WebView
  cacheEnabled={true}
  cacheMode="LOAD_CACHE_ELSE_NETWORK"
/>

Memory Management

Unity WebGL can be memory-intensive. Monitor memory usage and reload WebView if needed.

Loading Optimization

Show splash screen during initial load:
import * as SplashScreen from 'expo-splash-screen';

SplashScreen.preventAutoHideAsync();
// Hide when WebView loads:
SplashScreen.hideAsync();

Unity WebGL Mobile Optimization

Unity Mobile Optimizations
// Detect mobile in Unity and adjust settings
if (Application.platform == RuntimePlatform.WebGLPlayer) {
    // Reduce quality for mobile
    if (IsMobileDevice()) {
        QualitySettings.SetQualityLevel(1); // Low quality
        Application.targetFrameRate = 30;   // 30 FPS instead of 60
    }
}

bool IsMobileDevice() {
    string userAgent = Application.platform.ToString();
    return userAgent.Contains("Android") || userAgent.Contains("iOS");
}

Building for Production

Build Android APK

1

Configure Build

Ensure app.json has correct Android configuration.
2

Build with EAS

# Install EAS CLI
npm install -g eas-cli

# Login to Expo
eas login

# Configure build
eas build:configure

# Build APK
eas build --platform android --profile preview
3

Download APK

EAS Build will provide a download link when complete.
4

Test APK

Install the APK on Android device:
adb install app.apk

Publish to Expo Go

# Publish update to Expo Go
npx expo publish

# Users can access via Expo Go app
# using your published URL
For production release to Google Play Store or Apple App Store, use EAS Build to create native binaries.

Troubleshooting

Symptoms: Blank WebView or error messageSolutions:
  • Check network connection
  • Verify game URL is accessible
  • Check if JavaScript is enabled
  • Look for CORS errors in logs
  • Try clearing app cache
Symptoms: Touch input doesn’t workSolutions:
  • Verify javaScriptEnabled={true}
  • Check Unity build for mobile compatibility
  • Test with scalesPageToFit={true}
  • Review Unity Input settings
Symptoms: Laggy gameplay, low FPSSolutions:
  • Enable hardware acceleration
  • Reduce Unity quality settings
  • Lower target frame rate
  • Optimize Unity assets
  • Test on physical device (not emulator)
Symptoms: Virtual keyboard doesn’t showSolutions:
  • Add hidden input element in HTML
  • Use Keyboard API to show keyboard
  • Ensure input is focused
  • Check device keyboard settings

Next Steps

Deployment Guide

Deploy the web build for the mobile app to load

Unity Setup

Review Unity WebGL configuration details

Android Platform

User guide for Android platform

iOS Platform

Coming soon: iOS platform guide

Build docs developers (and LLMs) love