Membangun satu produk lengkap dari repo React: autentikasi biometric, push notification, offline CRUD dengan sync, deep link untuk share, theming, yang dirilis ke TestFlight + internal testing Play Store dengan CI/CD + live updates aktif.

Setelah di episode 24 kita menguasai migrasi dan upgrade, pada episode ini kita menggabungkan seluruh pengetahuan ke dalam satu proyek capstone: aplikasi production-grade yang menggunakan semua fitur Capacitor yang telah dipelajari. Ini adalah ujian akhir — dan portfolio kalian.
Mengapa capstone penting? Karena teori tanpa praktik lengkap hanya setengah pemahaman. Dengan membangun aplikasi dari nol sampai store, kalian akan menghadapi dan menyelesaikan masalah nyata yang tidak muncul di tutorial terisolasi.
Kita akan membangun TaskFlow — aplikasi manajemen tugas dengan fitur:
| Fitur | Plugin/Tech |
|---|---|
| Auth biometric (Face ID/fingerprint) | @capacitor-community/biometric |
| Push notification | @capacitor/push-notifications |
| Offline CRUD + sync | SQLite + Sync Engine |
| Deep link sharing | Capacitor deep links |
| Dark/light theming | CSS variables + StatusBar |
| Splash screen | @capacitor/splash-screen |
| Camera (foto profil) | @capacitor/camera |
| Secure token storage | @capacitor-secure-storage |
npx create-vite@latest taskflow --template react-ts
cd taskflow
npm install
npm install @capacitor/core @capacitor/cli
npx cap init "TaskFlow" "com.example.taskflow" --web-dir dist
# Plugins
npm install @capacitor/camera @capacitor/filesystem @capacitor/push-notifications
npm install @capacitor/splash-screen @capacitor/status-bar @capacitor/keyboard
npm install @capacitor/preferences @capacitor/share @capacitor/geolocation
npm install @capacitor-community/biometric @capacitor-community/sqlite
npm install @capacitor-secure-storage
npx cap add ios
npx cap add android
npx cap syncimport { BiometricAuth, BiometricType } from '@capacitor-community/biometric';
import { SecureStoragePlugin } from '@capacitor-secure-storage';
async function authenticateUser() {
const available = await BiometricAuth.isAvailable();
if (available.biometricType !== BiometricType.None) {
const result = await BiometricAuth.authenticate({
reason: 'Autentikasi untuk mengakses tugas Anda',
title: 'TaskFlow',
cancelTitle: 'Batal',
});
if (result.success) {
const token = await SecureStoragePlugin.get({ key: 'auth_token' });
return token.value;
}
}
// Fallback ke PIN
return await pinAuthentication();
}import { SQLite } from '@capacitor-community/sqlite';
async function initDatabase() {
const db = await SQLite.createConnection({ database: 'taskflow', version: 1 });
await db.execute(`
CREATE TABLE IF NOT EXISTS tasks (
id TEXT PRIMARY KEY,
title TEXT NOT NULL,
completed INTEGER DEFAULT 0,
synced INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
return db;
}
// Create (offline)
async function createTask(title: string) {
const db = await initDatabase();
const id = crypto.randomUUID();
await db.run(
'INSERT INTO tasks (id, title, completed, synced) VALUES (?, ?, 0, 0)',
[id, title]
);
return id;
}
// Read (offline)
async function getTasks() {
const db = await initDatabase();
const result = await db.query('SELECT * FROM tasks ORDER BY created_at DESC');
return result.values;
}import { Network } from '@capacitor/network';
class TaskSyncEngine {
async start() {
Network.addListener('networkStatusChange', async (status) => {
if (status.connected) {
await this.syncPendingTasks();
}
});
}
async syncPendingTasks() {
const db = await initDatabase();
const unsynced = await db.query('SELECT * FROM tasks WHERE synced = 0');
for (const task of unsynced.values) {
try {
await fetch('https://api.taskflow.app/tasks', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(task),
});
await db.run('UPDATE tasks SET synced = 1 WHERE id = ?', [task.id]);
} catch {
// Retry on next cycle
}
}
}
}import { Share } from '@capacitor/share';
async function shareTask(task: Task) {
await Share.share({
title: task.title,
text: `Check out my task: ${task.title}`,
url: `https://taskflow.app/task/${task.id}`,
});
}import { StatusBar, Style } from '@capacitor/status-bar';
function applyTheme(isDark: boolean) {
document.documentElement.classList.toggle('dark', isDark);
StatusBar.setStyle({ style: isDark ? Style.Light : Style.Dark });
StatusBar.setBackgroundColor({
color: isDark ? '#1e1e2e' : '#ffffff',
});
}name: Release TaskFlow
on:
push:
tags: ['v*']
jobs:
deploy:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- run: npx cap sync ios
- run: fastlane ios beta
- run: capgo upload --channel productionTip
Deploy ke TestFlight + Play Console internal testing secara bersamaan. Monitor crash rate selama 48 jam sebelum promote ke production.
Pada episode 25 ini, kalian telah membangun:
Di episode 26 selanjutnya, kita akan membahas ekosistem dan tren modern 2026 — Capacitor 8.5 stable, Ionic Framework 9, Capgo live updates, enterprise plugins, Portals micro-frontend, dan adopsi SPM luas. Sampai jumpa!