Camera, GPS & Sensors — Mobile

Akses Hardware: Camera, GPS & Sensors Salah satu keunggulan utama mobile app dibanding web app adalah akses penuh ke hardware device. Expo menyediakan API…

Kamera, GPS, dan sensor adalah perangkat keras HP yang bisa dipakai aplikasi, asal pemiliknya mengizinkan. Laundry Bu Rini memakai dua: kamera untuk scan QR di nota cucian, dan lokasi untuk alamat jemput. Bayangin kurir Bu Rini yang mau menjemput cucian. Dia gak boleh masuk rumah tanpa izin, dia harus bilang untuk apa ("saya mau ambil cucian"), dan Mbak Wulan boleh memberi alamat persis atau cuma "dekat masjid". Aplikasi juga begitu: minta izin, tulis alasannya, dan terima kalau pengguna cuma memberi lokasi perkiraan.

🧪 Tebakan cepat: Tombol "Pakai lokasi saat ini" memanggil Location.getCurrentPositionAsync() tanpa opsi apa pun. Titik yang didapat kurir akurat sampai berapa meter? Jawabannya ada di bagian "Lokasi untuk alamat jemput".

Izin dulu, dengan alasan yang jelas

npx expo install expo-camera expo-location   # SDK 57: 57.0.6 dan 57.0.20
{
  "expo": {
    "plugins": [
      ["expo-camera", { "cameraPermission": "Izinkan Laundry Bu Rini memotret nota cucianmu.", "microphonePermission": false, "recordAudioAndroid": false }],
      ["expo-location", { "locationWhenInUsePermission": "Izinkan Laundry Bu Rini memakai lokasimu untuk alamat jemput." }]
    ]
  }
}

Setelah npx expo prebuild, Info.plist berisi NSCameraUsageDescription dan NSLocationWhenInUseUsageDescription dengan dua kalimat itu, dan AndroidManifest.xml berisi CAMERA, ACCESS_COARSE_LOCATION, serta ACCESS_FINE_LOCATION. Kalimat alasan itu yang tampil di dialog iOS. Tanpa opsi itu, plugin menulis teks bawaan bahasa Inggris, misalnya Allow $(PRODUCT_NAME) to access your camera.

Scan QR nota dengan expo-camera

Setiap nota punya QR berisi kode order, misalnya LBR-13.

// src/components/ScanNota.tsx
import { CameraView, useCameraPermissions, type BarcodeScanningResult } from 'expo-camera';
import { router } from 'expo-router';
import { useRef } from 'react';
import { Button, Linking } from 'react-native';

const POLA_NOTA = /^LBR-(\d{1,6})$/; // isi QR di nota: LBR-13

export default function ScanNota() {
  const [izin, mintaIzin] = useCameraPermissions();
  const sudahScan = useRef(false);

  if (!izin) return null; // status izin belum terbaca
  if (!izin.granted) {
    return izin.canAskAgain
      ? <Button title="Izinkan kamera untuk scan nota" onPress={mintaIzin} />
      : <Button title="Buka Pengaturan" onPress={() => Linking.openSettings()} />;
  }

  function onScan({ data }: BarcodeScanningResult) {
    if (sudahScan.current) return; // terpanggil terus selama QR terlihat
    const cocok = POLA_NOTA.exec(data);
    if (!cocok) return; // QR lain (promo, link asing): abaikan
    sudahScan.current = true;
    router.push(`/order/${cocok[1]}`); // server tetap memeriksa: order orang lain = 404
  }

  return <CameraView style={{ flex: 1 }} facing="back" barcodeScannerSettings={{ barcodeTypes: ['qr'] }} onBarcodeScanned={onScan} />;
}

Lokasi untuk alamat jemput

// src/lib/lokasi.ts
import * as Location from 'expo-location';

export async function lokasiJemput() {
  const izin = await Location.requestForegroundPermissionsAsync();
  if (!izin.granted) return null; // tetap bisa ketik alamat manual

  const posisi = await Location.getCurrentPositionAsync({ accuracy: Location.Accuracy.High }); // sekitar 10 m
  const { latitude, longitude, accuracy } = posisi.coords; // accuracy: radius ketidakpastian, dalam meter
  const [alamat] = await Location.reverseGeocodeAsync({ latitude, longitude });
  return { latitude, longitude, akurasiMeter: accuracy, alamat };
}
Location.Accuracy Nilai Akurat sampai
Lowest 1 3 km
Low 2 1 km
Balanced 3 100 m (bawaan)
High 4 10 m
Highest 5 yang terbaik yang tersedia
BestForNavigation 6 tertinggi, plus data sensor, untuk navigasi

Tanpa opsi, getCurrentPositionAsync() memakai Balanced: akurat sampai sekitar 100 meter, cukup untuk "kelurahan mana", terlalu kasar untuk pagar rumah Mbak Wulan. Itu jawaban tebakannya. Untuk alamat jemput, minta High sekali saja.

Sensor: pakai kalau memang perlu

expo-sensors (57.0.3) memberi Accelerometer, Barometer, DeviceMotion, Gyroscope, LightSensor, Magnetometer, dan Pedometer. Aplikasi laundry ini gak butuh satu pun, jadi gak dipasang. Kalau suatu fitur butuh (misalnya goyang HP untuk lapor masalah), lepas listener-nya dengan sub.remove() saat layarnya ditinggal, isi motionPermission untuk iOS, dan ingat Android 12 ke atas membatasi pembacaan di 200 Hz.

Simulator dan emulator

Simulator iOS Emulator Android HP asli
Kamera dan scan QR gak ada kamera dokumentasi expo-camera menandainya "device only" ya
Lokasi pilih lewat menu Device > Location nyalakan Location di pengaturan emulator, atur titiknya dari emulator ya, termasuk pilihan "perkiraan"

Jadi scan nota diuji di HP asli. Lokasi bisa diuji di simulator, dengan titik buatan.

⚠️ Jebakan yang sering ketemu

TL;DR: Kamera dan lokasi butuh izin plus kalimat alasan dari config plugin (cameraPermission, locationWhenInUsePermission), yang ditulis ke Info.plist dan AndroidManifest.xml saat prebuild. iOS bertanya sekali, Android dua kali, setelah itu Linking.openSettings(). Library kamera yang umum: expo-camera atau react-native-vision-camera. CameraView dengan barcodeTypes: ['qr'] membaca LBR-13 dari nota; kunci setelah scan pertama, periksa polanya, dan biarkan server menjawab 404 untuk order orang lain. getCurrentPositionAsync() bawaannya Balanced (sekitar 100 m); untuk alamat jemput minta Accuracy.High (sekitar 10 m) sekali saja, dan siapkan alamat manual untuk lokasi perkiraan. Sensor cuma dipasang kalau fiturnya butuh. Scan diuji di HP asli; lokasi bisa diuji di simulator.

Yang akan kamu pelajari