š Overview
Di bagian final ini, kita akan:
- ā Polish UI (loading states, empty states, transitions)
- ā Improve error handling
- ā Build APK untuk internal testing
- ā Build AAB untuk Play Store
- ā Publish ke Google Play Store
⨠Step 1: Polish UI
A. Tambahkan Splash Screen Custom
File: app.json - update splash configuration:
{
"expo": {
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#1a1a1a"
}
}
}
B. Buat Empty State Component
File: components/EmptyState.tsx
import React from 'react';
import { View, Text } from 'react-native';
import { LucideIcon } from 'lucide-react-native';
interface EmptyStateProps {
icon: LucideIcon;
title: string;
description?: string;
}
export function EmptyState({ icon: Icon, title, description }: EmptyStateProps) {
return (
<View className="flex-1 items-center justify-center py-20 px-8">
<Icon size={64} color="#4B5563" />
<Text className="text-white text-lg font-semibold mt-4 text-center">
{title}
</Text>
{description && (
<Text className="text-gray-400 text-center mt-2">
{description}
</Text>
)}
</View>
);
}
C. Buat Error Boundary Component
File: components/ErrorBoundary.tsx
import React, { Component, ReactNode } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { AlertTriangle } from 'lucide-react-native';
interface Props {
children: ReactNode;
}
interface State {
hasError: boolean;
error?: Error;
}
export class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error: Error): State {
return { hasError: true, error };
}
handleRetry = () => {
this.setState({ hasError: false, error: undefined });
};
render() {
if (this.state.hasError) {
return (
<View className="flex-1 items-center justify-center bg-secondary px-8">
<AlertTriangle size={64} color="#ef4444" />
<Text className="text-white text-xl font-bold mt-4 text-center">
Oops! Terjadi Kesalahan
</Text>
<Text className="text-gray-400 text-center mt-2">
{this.state.error?.message || 'Something went wrong'}
</Text>
<TouchableOpacity
className="bg-primary px-8 py-4 rounded-xl mt-6"
onPress={this.handleRetry}
>
<Text className="text-white font-bold">Coba Lagi</Text>
</TouchableOpacity>
</View>
);
}
return this.props.children;
}
}
D. Network Status Indicator
File: components/NetworkStatus.tsx
import React, { useEffect, useState } from 'react';
import { View, Text, Animated } from 'react-native';
import NetInfo from '@react-native-community/netinfo';
import { WifiOff } from 'lucide-react-native';
export function NetworkStatus() {
const [isConnected, setIsConnected] = useState(true);
const opacity = new Animated.Value(0);
useEffect(() => {
const unsubscribe = NetInfo.addEventListener((state) => {
setIsConnected(state.isConnected ?? true);
});
return () => unsubscribe();
}, []);
useEffect(() => {
Animated.timing(opacity, {
toValue: isConnected ? 0 : 1,
duration: 300,
useNativeDriver: true,
}).start();
}, [isConnected]);
if (isConnected) return null;
return (
<Animated.View
style={{ opacity }}
className="absolute top-0 left-0 right-0 bg-red-600 py-2 px-4 flex-row items-center justify-center z-50"
>
<WifiOff size={16} color="#fff" />
<Text className="text-white ml-2 font-medium">
Tidak ada koneksi internet
</Text>
</Animated.View>
);
}
Install NetInfo:
npx expo install @react-native-community/netinfo
š§ Step 2: Improve Error Handling
A. Global Error Handler
File: utils/errorHandler.ts
import { Alert } from 'react-native';
export function handleApiError(error: any, fallbackMessage = 'Terjadi kesalahan') {
console.error('API Error:', error);
let message = fallbackMessage;
if (error.response) {
// Server responded with error
message = error.response.data?.error || error.response.data?.message || fallbackMessage;
if (error.response.status === 401) {
// Token expired - handled by interceptor
return;
}
if (error.response.status === 403) {
message = 'Anda tidak memiliki akses';
}
if (error.response.status === 404) {
message = 'Data tidak ditemukan';
}
if (error.response.status >= 500) {
message = 'Server sedang bermasalah. Coba lagi nanti.';
}
} else if (error.request) {
// No response received
message = 'Tidak dapat terhubung ke server. Periksa koneksi internet.';
}
Alert.alert('Error', message);
}
B. Retry Logic untuk API Calls
File: utils/retry.ts
export async function withRetry<T>(
fn: () => Promise<T>,
retries = 3,
delay = 1000
): Promise<T> {
try {
return await fn();
} catch (error) {
if (retries > 0) {
await new Promise((resolve) => setTimeout(resolve, delay));
return withRetry(fn, retries - 1, delay * 2);
}
throw error;
}
}
š¦ Step 3: Build APK untuk Testing
A. Setup EAS Build
# Install EAS CLI
npm install -g eas-cli
# Login ke Expo account
eas login
# Initialize EAS di project
eas build:configure
B. Konfigurasi eas.json
File: eas.json
{
"cli": {
"version": ">= 5.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"preview": {
"distribution": "internal",
"android": {
"buildType": "apk"
}
},
"production": {
"android": {
"buildType": "app-bundle"
}
}
},
"submit": {
"production": {
"android": {
"serviceAccountKeyPath": "./play-store-key.json"
}
}
}
}
C. Build APK Preview
# Build APK untuk internal testing
eas build --platform android --profile preview
Setelah selesai, download APK dari link yang diberikan.
šŖ Step 4: Build AAB untuk Play Store
A. Persiapan Production
- Update app.json untuk production:
{
"expo": {
"name": "Lampung Dev",
"slug": "lampung-dev",
"version": "1.0.0",
"android": {
"package": "org.lampungdev.mobile",
"versionCode": 1,
"permissions": ["CAMERA"],
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#1a1a1a"
}
}
}
}
- Update API URL untuk production di
services/api.ts:
export const BASE_URL = __DEV__
? 'http://192.168.1.100:3000'
: 'https://lampungdev.org'; // Production URL
B. Build Production AAB
# Build AAB untuk Play Store
eas build --platform android --profile production
Proses ini memakan waktu 10-20 menit. Setelah selesai, download AAB dari link yang diberikan.
š Step 5: Publish ke Play Store
A. Persiapan Play Console
- Buka Google Play Console
- Create new app
- Fill app details:
- App name: Lampung Dev
- Default language: Indonesian
- App or game: App
- Free or paid: Free
B. Store Listing
Siapkan aset berikut:
| Asset | Ukuran | Format |
|---|---|---|
| App icon | 512x512 | PNG |
| Feature graphic | 1024x500 | PNG |
| Phone screenshots | Min 2, max 8 | PNG |
| Short description | Max 80 chars | Text |
| Full description | Max 4000 chars | Text |
Contoh Short Description:
Aplikasi admin untuk scan kehadiran peserta event LampungDev
Contoh Full Description:
Lampung Dev Mobile adalah aplikasi resmi untuk admin dan panitia komunitas Lampung Dev.
Fitur:
ā
Lihat daftar event yang akan datang
ā
Lihat detail event dan peserta terdaftar
ā
Scan QR code untuk verifikasi kehadiran peserta
ā
Catat waktu check-in secara real-time
Aplikasi ini hanya untuk admin/panitia event. Untuk mendaftar event, kunjungi lampungdev.org.
C. Upload AAB
- Go to Production ā Releases
- Create new release
- Upload AAB file
- Add release notes
- Review and rollout
D. Content Rating & Privacy
- Complete content rating questionnaire
- Add privacy policy URL
- Complete data safety form
š Step 6: Post-Launch
A. Monitor Crashes
Gunakan EAS Insights atau Firebase Crashlytics:
# Install expo-insights
npx expo install expo-insights
# Enable di app.json
{
"expo": {
"plugins": ["expo-insights"]
}
}
B. App Updates
Untuk update aplikasi:
# Increment version di app.json
"version": "1.0.1",
"android": {
"versionCode": 2
}
# Build ulang
eas build --platform android --profile production
# Upload ke Play Console
C. OTA Updates (Over-the-Air)
Untuk update tanpa Play Store review:
# Publish OTA update
eas update --branch production --message "Bug fixes"
ā Final Checklist
Pre-Launch
- UI polish (empty states, loading, errors) ā
- Error handling improved ā
- APK tested di device fisik
- No crashes atau major bugs
- API production ready
Play Store
- App icon 512x512
- Feature graphic 1024x500
- Screenshots (min 2)
- Short & full description
- Privacy policy URL
- Content rating complete
- AAB uploaded
Post-Launch
- Monitor crash reports
- Respond to user reviews
- Plan next features
š Selamat!
Kamu telah berhasil membangun aplikasi event management lengkap dengan React Native Expo!
Apa yang Sudah Kamu Pelajari:
- Arsitektur - System design untuk mobile app
- Setup - Environment development di berbagai OS
- Project Setup - Expo, NativeWind, folder structure
- Authentication - Login, Zustand, SecureStore
- Event Management - Lists, cards, detail screens
- QR Scanner - Camera, barcode scanning, feedback
- Deployment - Build, publish ke Play Store
Source Code
Repository lengkap tersedia di: š github.com/Lampung-Dev/lampung-dev-mobile
Kontribusi
Tertarik berkontribusi ke project open source LampungDev? Join komunitas kami:
- Website: lampungdev.org
- Instagram: @lampungdev
- GitHub: github.com/Lampung-Dev
Semua Parts dalam Series Ini
| Part | Topik |
|---|---|
| 1 | Pengenalan & Arsitektur |
| 2 | Instalasi di Mac, Windows, Ubuntu |
| 3 | Project Setup |
| 4 | Autentikasi Admin |
| 5 | Manajemen Event |
| 6 | QR Scanner Kehadiran |
| 7 | Polish & Deploy |
Happy Coding! š
Series ini dibuat untuk mendukung materi Casual Meetup #15 di LampungDev oleh Muhammad Fari Madyan.
