Menuntaskan janji utama desktop development: strategi satu codebase yang benar-benar berjalan di Windows, macOS, dan Linux lewat layer abstraksi, deteksi platform, matriks uji penuh, dan adaptasi perilaku per OS

Di episode 24 kita mengelola banyak window dengan benar. Tapi ada janji yang belum kita tuntaskan sejak episode 0: satu codebase yang berjalan di Windows, macOS, dan Linux. Janji ini mudah disalahartikan — "write once, run anywhere" terdengar seperti menulis sekali dan semuanya selesai. Kenyataannya lebih halus: write once, adapt everywhere.
Episode ini membedah cross-platform mastery: layer abstraksi yang menyerap perbedaan OS, teknik deteksi platform, area yang paling sering meleset (shortcut, path, dialog, theming), dan matriks uji penuh yang membuktikan aplikasi kalian benar di semua platform — bukan hanya di OS tempat kalian develop.
Prinsip intinya: platform differences diserap oleh layer abstraksi, sementara UI dan logika bisnis tetap satu. Kalian mendefinisikan antarmuka (interface) untuk setiap capability sistem, lalu menulis implementasi per platform di belakangnya — pola yang sama seperti storage di episode 8 dan plugin SDK di episode 23.
export interface PlatformService {
openPathDialog(): Promise<string | null>;
notify(title: string, body: string): Promise<void>;
getDataDir(): Promise<string>;
getPrimaryModifier(): "ctrl" | "meta";
openExternal(url: string): Promise<void>;
}UI memanggil PlatformService, bukan implementasi spesifik OS. Ketika macOS butuh meta untuk shortcut dan Windows butuh ctrl, hanya implementasi yang berubah — komponen UI tidak tersentuh.
Ada dua gaya deteksi:
Gunakan deteksi platform untuk hal yang memang melekat pada OS (modifier key, dialog style), dan deteksi fitur untuk hal teknis (dukungan GPU, format notification).
Ini peta area yang hampir selalu meleset antar OS:
| Area | Windows | macOS | Linux |
|---|---|---|---|
| Modifier shortcut | Ctrl | Cmd (meta) | Ctrl |
| Path data aplikasi | %APPDATA% | ~/Library/Application Support | ~/.config |
| Titlebar native | SwiftUI? → WinUI style | traffic-light buttons | WM-dependent |
| Notification | Windows toast | UserNotifications | libnotify |
| Tema terang/gelap | Registry/apps | System appearance | GTK/Qt theme |
| Menu bar | In-window | Global menu bar | In-window |
Banyak perbedaan ini sudah ditangani framework (menus, notifications). Tugas kalian adalah tidak meng-hardcode yang seharusnya di-delegasikan, dan menulis implementasi per platform untuk yang benar-benar berbeda.
Cara menulis cabang platform di tiap framework CatatanKu:
#[cfg(target_os = "macos")]
fn primary_modifier() -> &'static str { "meta" }
#[cfg(not(target_os = "macos"))]
fn primary_modifier() -> &'static str { "ctrl" }
// di webview: window.navigator.userAgent atau TAURI_ENV_PLATFORMPerhatikan pola di Electron: renderer tidak membaca process.platform untuk keputusan UI penting — ia bertanya ke main process lewat IPC. Alasan keamanannya persis seperti episode 18 dan 24: renderer seharusnya tidak menggenggam informasi sistem yang bisa dipalsukan; satu sumber kebenaran di main/core.
Tip
Definisikan shortcut dengan abstraksi aksi, bukan kombinasi mentah. Simpan action = save di config, lalu mapping ke kombinasi tombol per platform. Begitu pengguna bisa mengubah shortcut via settings, kalian tidak perlu menulis ulang logika di mana-mana.
Menulis kode lintas platform tanpa menguji di semua platform adalah perjudian. Kembali ke episode 17, CI kita sudah membangun artefak per OS — sekarang perkuat dengan matriks uji:
name: cross-platform-tests
on: [push, pull_request]
jobs:
test:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- run: bun install --frozen-lockfile
- run: bun run build
- run: bun run test:unit # episode 15
- run: bun run test:e2e # Playwright lintas OSYang diuji di semua OS:
PlatformService — gunakan fake per platform sehingga logika bisnis teruji tanpa OS nyata.Satu codebase tidak berarti satu artefak. Tiap OS punya bentuk distribusinya sendiri (episode 12): .exe/.msi untuk Windows, .dmg/.pkg untuk macOS, .deb/.AppImage/.rpm untuk Linux — masing-masing dengan signing dan notarization-nya. CI matrix menghasilkan semuanya dari kode yang sama; itulah makna sebenarnya dari cross-platform mastery.
path.join), bukan string dengan / atau \.Ctrl untuk semua OS — pengguna macOS akan frustrasi; hormati meta/Cmd.Inti yang harus dibawa pulang:
PlatformService, UI dan logika bisnis tetap satu.Di episode 26 berikutnya kita melangkah mundur melihat gambaran besar: ekosistem & tren modern 2026 — peta posisi Tauri 2, Electron, Flutter, dan arah industri desktop ke depan. Sampai jumpa di episode 26!