Mobile

Membangun Geofence di Aplikasi Driver Container: Pengalaman dan Tantangannya di Expo React Native

admin
admin

1 Sep 202612 min baca

Membangun Geofence di Aplikasi Driver Container: Pengalaman dan Tantangannya di Expo React Native

Beberapa waktu lalu saya ngerjain satu fitur yang kelihatannya simple banget di atas kertas: aplikasi driver container yang bisa tahu sendiri posisi truknya, lalu otomatis mengupdate status tugas begitu truk masuk area tertentu. Fitur itu namanya geofence. Konsepnya sebaris kalimat, implementasinya berminggu-minggu. Di artikel ini saya mau sharing pengalaman lengkapnya, dari kenalan dulu sama geofence, kenapa dia berguna, gimana penerapannya di Expo React Native, tantangan yang beneran saya hadapi, sampai akhirnya bagaimana fitur itu bisa jalan di production. Semoga berguna buat kamu yang mau atau lagi ngerjain hal serupa.

Geofence itu apa sih?

Bayangin kamu gambar lingkaran di peta. Misalnya lingkaran radius 100 meter dengan pusat di sebuah depo. Itulah geofence: pagar virtual. Begitu sebuah perangkat masuk atau keluar dari lingkaran itu, sistem bisa menjalankan sesuatu. Masuk area, trigger ENTER. Keluar area, trigger EXIT. Sesederhana itu.

Yang sering salah dipahami: geofence itu bukan aplikasi yang nyalain GPS terus terusan dan ngitung jarak tiap detik. Kalau kayak gitu baterai HP bakal habis dalam hitungan jam. Cara kerjanya kebalikannya: kita cuma daftarkan koordinat dan radius ke sistem operasi, terus aplikasinya tidur. Yang jaga pergerakan perangkat itu OS-nya sendiri, lewat kombinasi sinyal menara seluler, wifi, dan GPS hemat daya. Cuma pas perangkat beneran nyebrang batas area, OS baru bangunin aplikasi dan kasih tau: eh, dia masuk areanya. Efisien banget soal baterai, tapi konsekuensinya ada banyak jebakan teknis yang akan saya ceritain nanti.

Geofence itu berguna buat apa aja?

Sebelum cerita study case, biar kebayang manfaatnya, ini beberapa pemakaian geofence yang paling umum di dunia nyata:

Absensi berbasis lokasi: karyawan cukup berada di kantor, sistem langsung tandai hadir. Nggak bisa diakali dari rumah, karena absennya nempel koordinat.

Marketing dan promo: aplikasi belanja ngirim notifikasi diskon pas kamu lagi lewat deket mall yang jadi mitranya. Timing-nya pas, karena lokasinya pas.

Logistik dan armada: ini yang paling deket sama kasus saya. Perusahaan pengiriman, taksi online, atau rental kendaraan pakai geofence buat tahu kendaraannya udah nyampe gudang, keluar area parkir, atau masuk zona terlarang, semua tanpa operator ngecek manual.

Keamanan dan pemantauan: anak keluar dari radius rumah, orang tua dapat notifikasi. Barang berharga keluar dari area gudang, tim keamanan dapet peringatan.

Intinya sama di semua kasus: geofence itu mengubah lokasi jadi pemicu aksi otomatis. Yang biasanya harus diinput manusia, sekarang terjadi sendiri karena sistem “melihat” posisinya.

Study case saya: aplikasi driver container

Sekarang konteksnya. Aplikasi yang saya kerjain dipakai driver truk container. Kalau belum familiar dengan dunia logistik ekspor, kira-kira begini alur kerjanya: satu tugas container itu jalan lewat beberapa tahap. Pertama driver ambil container kosong di depo. Kedua, container itu dibawa ke gudang buat diisi barang, istilah kerennya stuffing. Terakhir, container yang udah penuh diantar ke container yard (CY) di pelabuhan, buat menunggu naik kapal. Di sistem, tiap tahap itu punya status: Job Baru, lalu Proses Ambil Empty Container, lalu Proses Stuffing, lalu Container Masuk ke CY. Urutan status ini penting banget karena jadi dasar timeline tracking yang dipakai perusahaan buat memantau progres pengiriman.

Masalahnya, update statusnya dulu manual. Tiap sampai lokasi, driver harus buka aplikasi dan update tugasnya. Realitanya di lapangan: driver itu lagi nyetir, capek, sibuk, dan HP-nya sering dikantongin. Jadinya banyak status yang telat, terlupa, atau nggak akurat. Timeline tracking jadi kacau, dan tim operasional harus ngelewatin waktu buat ngejar-ngege driver biar update.

Nah, kebetulan sistem udah punya asset yang berharga: data master lokasi. Datanya isinya begini: nama lokasi, koordinat latitude longitude, dan status tahap yang sesuai dengan lokasi itu. Contohnya, puluhan depo container di area Marunda sampai Cakung itu semua ditandai status PROSES AMBIL EMPTY CONTAINER. Gudang-gudang stuffing di Cikarang dan Cikampek ditandai PROSES STUFFING. Terminal pelabuhan kayak JICT, NPCT1, dan Koja ditandai CONTAINER MASUK KE CY. Jadi idenya sederhana: data ini tinggal dipakai jadi patokan geofencing. Tiap titik lokasi saya daftarin jadi lingkaran radius 100 meter. Kalau truk masuk lingkaran yang tepat, status tugasnya langsung dimajuin otomatis ke tahap berikutnya. Driver nggak perlu ngapain-ngapain.

Alur lengkapnya jadi gini: driver dapet tugas baru, statusnya Job Baru. Truk jalan, sampai depo, masuk radius 100 meter dari titik depo yang terdaftar. Aplikasi, walaupun lagi nggak dibuka, bangun di background, cek daftar tugas driver itu: ada yang statusnya Job Baru? Ada. Berarti statusnya dimajuin ke Proses Ambil Empty Container. Container kosong udah diambil, truk jalan ke gudang, masuk radius titik gudang stuffing. Aplikasi cek lagi: ada tugas yang statusnya sekarang pas dengan tahap sebelum stuffing? Ada, dimajuin ke Proses Stuffing. Terakhir, container penuh dibawa ke pelabuhan, masuk radius terminal CY, status dimajuin ke Container Masuk ke CY. Dan di titik ini update-nya berhenti: status udah sampai tujuan akhir, nggak dicek lagi. Satu tugas selesai sepenuhnya lewat gerakan truk doang.

Cara biasanya orang bikin geofence di Expo

Aplikasi ini dibangun pakai Expo. Dan kalau kamu cari tutorial geofence di Expo atau React Native, hampir semua yang ketemu polanya sama: pakai expo-location plus expo-task-manager. Kurang lebih kayak gini:

import * as TaskManager from "expo-task-manager";
import * as Location from "expo-location";

const GEOFENCE_TASK = "GEOFENCE_TASK";

TaskManager.defineTask(GEOFENCE_TASK, ({ data, error }) => {
  if (error) return;
  const { eventType, region } = data as any;
  if (eventType === Location.GeofencingEventType.Enter) {
    console.log("Masuk area:", region);
  }
});

await Location.startGeofencingAsync(GEOFENCE_TASK, [
  { identifier: "gudang-stuffing", latitude: -6.2, longitude: 106.8, radius: 200 },
]);

Kode ini beneran jalan, dan buat demo atau kebutuhan ringan itu cukup. Tapi begitu saya bawa ke skenario production, batasnya kelihatan satu per satu. Dan batas-batas itulah yang jadi tantangan utama proyek ini.

Tantangan implementasi di Expo dan Android

Tantangan pertama sekaligus paling mendasar: event kejadian pas aplikasinya udah nggak hidup. Bayangin driver buka aplikasi pagi hari, terus HP-nya dipakai buat hal lain seharian. Android itu agresif banget membunuh aplikasi yang nggak dipakai, demi hemat baterai dan memori. Jadi pas tengah hari truk masuk area geofence, aplikasinya udah lama mati. OS sebenernya tetep ngasih tau lewat sinyal khusus di sisi native, tapi di sinilah masalahnya: saya develop pakai Expo, dan di Expo semua logika bisnis hidup di JavaScript. JavaScript itu nggak otomatis nyala pas sinyalnya dateng. Jadi yang terjadi: OS teriak “ada yang masuk area!”, nggak ada yang jawab, event-nya ilang begitu aja. Status tugas nggak keupdate, dan truk udah melaju tinggalkan lokasi.

Nah, kalau tantangan pertama bisa dijawang dengan mindahin logika ke native, tantangan kedua langsung nyusul: kode native yang dibangunin OS itu datang dengan tangan kosong. Dia nggak punya token login. Dia nggak tahu driver siapa yang harus dicek tugasnya. Dia juga nggak tahu harus lapor ke server yang mana. Padahal tugas justru sebaliknya: dia harus bisa ngecek daftar tugas driver tertentu, terus mengupdate statusnya di server atas nama driver itu. Jelas nggak mungkin dia kerja dalam keadaan “amnesia” kayak gitu.

Tantangan ketiga bikin saya sadar satu hal: musuh terbesar bukan selalu teknologi, kadang cuma data. Data master lokasi itu diinput manusia dari puluhan depo dan gudang. Sekali-sekali ada yang ngisi sembarangan: koordinat kosong, koordinat nol persis yang kalau dipetain posisinya di tengah laut, atau dua gudang beda yang keisi koordinat sama persis. Kelihatannya sepele, tapi satu baris data jelek yang lolos bisa bikin seluruh proses pendaftaran geofence gagal. Dan yang bikin kesel: gagalnya diem-diem, nggak ada error apa pun yang kelihatan di aplikasi. Fiturnya cuma berhenti bekerja tanpa pamit.

Tantangan keempat: GPS-nya sendiri suka nggak jujur. Di area padet kayak sekitar pelabuhan yang gedung-gedungnya tinggi, koordinat yang dilaporkan bisa “mental” bolak-balik dalam radius beberapa puluh meter. Tiap koordinat nyebrang batas area, OS nganggepnya transisi beneran. Akibatnya dalam dua menit bisa kecatat masuk, keluar, masuk lagi. Kalau semua event itu langsung dipercaya, status tugas bisa maju-mundur nggak karuan dan notifikasi ke driver jadi spam.

Tantangan terakhir, dan yang paling nggak bisa dilogika: tingkah produsen HP. Dokumentasi Android jelas-jelas bilang geofence harus dijaga sistem. Tapi hampir tiap merk HP punya lapisan penghemat baterai sendiri yang dengan bangga melanggar aturan itu. Hasilnya: aplikasi saya mulus di HP yang dipakai para developer, tapi kacau di HP murah tertentu. Dan ironisnya, justru HP murah itu yang paling banyak dipakai driver di lapangan.

Bagaimana saya menyelesaikannya

Diagram alur update status tugas driver otomatis via geofence dari Job Baru sampai Container Masuk ke CY
Alur lengkapnya: dari data master lokasi, truk yang masuk radius 100 meter, sampai status tugas maju sendiri dan berhenti di CY.

Dari semua tantangan di atas, kesimpulannya satu: seluruh logika penting nggak boleh ada di JavaScript. Dia harus jalan di sisi native, supaya bisa hidup dan kerja sendirian walau aplikasi udah dibunuh. Jadi saya bikin custom native module Expo pakai Kotlin, dijalankan lewat custom dev client. Arsitekturnya tiga bagian.

Pertama, sisi JavaScript. Tugasnya cuma tiga: ambil data master lokasi beserta status tahapnya dari server, daftarin tiap titik sebagai geofence radius 100 meter ke OS lewat native module, dan nyimpen konfigurasi (token, identitas driver, URL API) ke sisi native. Semua itu dilakukan tiap kali aplikasi dibuka. Ini penting: aplikasi memang harus dibuka dulu minimal sekali, karena dari sanalah semua area didaftarkan dan konfigurasi disimpen.

Kedua, native module. Di Kotlin, pendaftaran area pakai GeofencingClient dengan expire never, jadi OS tetep inget semua areanya walaupun aplikasinya mati:

val geofence = Geofence.Builder()
  .setRequestId(id)
  .setCircularRegion(lat, lng, radius) // radius 100m dari data master
  .setTransitionTypes(
    Geofence.GEOFENCE_TRANSITION_ENTER or
    Geofence.GEOFENCE_TRANSITION_EXIT
  )
  .setExpirationDuration(Geofence.NEVER_EXPIRE)
  .build()

Ketiga, receiver. Ini bagian yang dibangunin OS pas truk nyebrang batas radius. Semua logika bisnis ada di sini, jalan dalam Kotlin tanpa JavaScript sama sekali. Urutan kerjanya: baca konfigurasi yang udah disimpen di SharedPreferences, ambil daftar tugas aktif driver dari API, terus cocokin. Ini bagian paling penting dari konsepnya: satu lokasi cuma boleh memajukan tugas yang statusnya emang satu tahap sebelum lokasi itu. Depo hanya relevan buat tugas yang statusnya Job Baru. Gudang stuffing hanya relevan buat tugas yang lagi Proses Ambil Empty Container. Terminal CY hanya buat yang lagi Proses Stuffing. Kalau nggak ada tugas yang cocok, misalnya driver cuma lewat deket depo tanpa tugas, nggak ada apa-apa yang berubah. Dan begitu status udah sampai Container Masuk ke CY, tugas itu udah selesai, nggak dicek lagi.

Logika pencocokan urutannya sederhana tapi nyesel kalau dilupakan. Di native, saya bikin peta urutan: area bertipe depo butuh tugas berstatus Job Baru, area stuffing butuh tugas yang udah di tahap ambil empty, area CY butuh tugas yang udah di tahap stuffing. Receiver tinggal nyari tugas driver yang last status-nya cocok, terus update yang cocok aja:

// peta urutan: lokasi ini boleh memajukan status apa
val statusSequence = mapOf(
  "proses ambil empty container" to "Job Baru",
  "proses stuffing" to "PROSES AMBIL EMPTY CONTAINER",
  "container masuk ke cy" to "PROSES STUFFING"
)

// saat masuk area: cari tugas yang statusnya
// satu tahap sebelum lokasi ini
val requiredPreviousStatus = statusSequence[areaStatus]
val matchingJobs = activeJobs.filter {
  it.lastStatus == requiredPreviousStatus
}

Karena receiver itu dibatasi waktunya cuma beberapa detik, semua pekerjaan berat dijalanin di thread terpisah dan ditandai goAsync() biar sistem nggak motong di tengah jalan. Setelah status keupdate, driver juga dapet notifikasi berisi info area yang dimasuki dan berapa tugas yang keupdate, jadi dia tetep tau apa yang terjadi tanpa buka aplikasi.

Buat data kotor, saya kasih filter dua lapis. Di JS sebelum daftar: koordinat harus valid, latitude dan longitude nggak boleh nol, radius harus lebih dari nol. Di Kotlin saya pasang filter yang sama sebagai lapis kedua:

if (lat.isNaN() || lng.isNaN() || lat < -90.0 || lat > 90.0 ||
    lng < -180.0 || lng > 180.0 || radius <= 0f) {
  return@mapNotNull null // skip data jelek
}

Terus gimana sama trigger palsu GPS yang saya ceritain di bagian tantangan? Saya pakai dua pertahanan. Pertama, setiap event yang datang dari OS selalu membawa informasi akurasi posisi. Kalau angka akurasinya jelek banget, misalnya radius ketidakpastiannya ratusan meter, event itu nggak saya langsung percaya. Kedua, ini yang paling ampuh, validasi urutan status yang udah saya jelasin di atas. Jadi misalnya GPS-nya mental dan truk kelihatan “masuk-pulang-masuk” terminal CY dalam dua menit, event itu nggak akan berpengaruh apa-apa, karena status tugasnya emang belum nyampe tahap stuffing. Saya suka membayangin urutan status itu kayak palang kereta: trigger boleh dateng kapan aja dan sebanyak apapun, tapi gerbong cuma bisa jalan lewat jalurnya sendiri.

Lalu bagaimana menghadapi produsen HP yang suka membunuh aplikasi background? Saya pakai foreground service: semacam proses yang tampil ke pengguna lewat notifikasi kecil yang nempel terus di bar notifikasi. Selama notifikasi itu ada, sistem nganggep aplikasi lagi kerja serius dan jadi segan membunuhnya. Di samping itu, pengguna juga saya mintain buat mengecualikan aplikasi dari pengoptimalan baterai di pengaturan HP. Ini bukan trik hack, memang begini cara resmi bertahan hidup di ekosistem Android.

Dengan semua setup di atas, siklus hidup fiturnya akhirnya kebentuk dan jelas. Aplikasi cukup dibuka sekali: di momen itu semua area didaftarkan ke OS dan konfigurasi disimpen. Setelah itu, aplikasinya boleh ditutup atau bahkan di-kill dari recent apps, prosesnya tetap jalan, karena geofence-nya terdaftar di OS dan seluruh logikanya udah pindah ke native. Satu-satunya yang bisa matiin semua ini cuma restart HP. Dan penanganannya pun simpel: pengguna tinggal buka aplikasinya sekali lagi, semua area otomatis terdaftar ulang.

Testingnya gimana?

Saya nggak perlu keluar rumah buat ngetes, karena ada mock location. Aktifkan developer mode Android, pasang aplikasi lokasi palsu, dan kita bisa simulasi perjalanan: atur titik awal, titik akhir, dan kecepatannya. HP diam di meja, aplikasinya ngira kita lagi jalan.

Caranya: saya daftarin area geofence di sekitar rumah, lalu jalankan simulasi yang lewat area itu. Saya coba beberapa skenario: lewat sekali, lewat balik lagi, berhenti lama di dalam area, dan jalan pelan di pinggir radius. Semua hasilnya kelihatan di log: event kecatat nggak, telat berapa lama, notifikasi muncul nggak.

Batasnya satu: perilaku pembunuh baterai tiap merk HP dan akurasi GPS antara gedung tetap harus dites di lapangan. Tapi buat ngetes logika inti, mock location udah cukup.

Pelajaran yang saya bawa

Fiturnya akhirnya jalan sesuai harapan. Truk masuk area yang tepat, status maju sendiri sesuai urutan, driver dapat notifikasi, dan tim operasional nggak perlu ngejar driver lagi.

Pelajaran terbesarnya: bikin fitur yang bisa diandalkan itu bukan soal kode yang pintar, tapi paham di kondisi apa kode kita bakal mati, lalu rancang biar pekerjaan tetap selesai. Bentuknya di proyek ini: logika di native, konfigurasi disimpen awal, data disaring, baterai dijaga.

Buat yang mau bikin serupa: pelajari dulu siklus hidup platformnya, jangan buru-buru milih library. Library gampang diganti, pemahaman itu fondasinya.

Artikel Terkait

(3)

Komentar

(0)

Komentar Anda akan dimoderasi.

Memuat komentar...