Episode ini membahas media: kamera dengan react-native-vision-camera, image picker, resize dan kompresi gambar, manajemen permission dengan react-native-permissions, deskripsi usage di Info.plist, serta UX saat izin ditolak user.

Kamera dan galeri adalah fitur yang paling sering memicu pertanyaan permission dan crash pertama di aplikasi mobile. Satu deklarasi yang salah di Info.plist atau AndroidManifest.xml, dan aplikasi langsung menutup saat user mengetuk tombol kamera.
Episode 12 membahas media secara utuh: mengakses kamera dengan react-native-vision-camera, memilih gambar dengan image picker, meresize dan mengompresi, lalu mengelola permission dengan react-native-permissions — termasuk deskripsi usage di Info.plist dan pengalaman user saat izin ditolak.
VisionCamera adalah library kamera modern berbasis JSI. Di project Expo, pasang lewat npx expo install agar versinya cocok dengan SDK:
npx expo install react-native-vision-cameraTambahkan deskripsi penggunaan kamera di Info.plist untuk iOS dan deklarasi permission di AndroidManifest.xml untuk Android sebelum menjalankan aplikasi.
import { Camera, useCameraDevice } from "react-native-vision-camera";
import { Text, View } from "react-native";
export function KameraScreen() {
const device = useCameraDevice("back");
if (device == null) {
return <Text>Kamera tidak tersedia</Text>;
}
return (
<View style={{ flex: 1 }}>
<Camera
style={{ flex: 1 }}
device={device}
isActive={true}
photo={true}
/>
</View>
);
}useCameraDevice("back") memilih kamera belakang. Properti isActive mengontrol kapan kamera hidup — matikan saat layar tidak tampil untuk menghemat baterai. photo={true} mengaktifkan mode pengambilan foto.
Path hasil takePhoto() dari ref kamera — file sementara — siap diolah lebih lanjut: resize, compress, atau upload ke server.
Tidak semua alur butuh kamera langsung. Untuk memilih gambar yang sudah ada di galeri, gunakan image picker:
npm install react-native-image-pickerimport { launchImageLibrary } from "react-native-image-picker";
function pilihGambar() {
launchImageLibrary({ mediaType: "photo" }, (res) => {
if (res.didCancel) return;
const uri = res.assets?.[0]?.uri;
prosesGambar(uri);
});
}Perhatikan res.didCancel — user menutup picker tanpa memilih adalah kejadian normal, bukan error. res.assets?.[0]?.uri menggunakan optional chaining karena array bisa kosong.
Gambar asli kamera modern berukuran belasan megabyte. Mengunggahnya mentah-mentah menghabiskan kuota dan memperlambat UI. Resize dan kompresi sebelum upload:
npm install react-native-image-resizerimport ImageResizer from "react-native-image-resizer";
async function siapkanUpload(uri) {
const hasil = await ImageResizer.createResizedImage(uri, 1200, 1200, "JPEG", 85);
return hasil.uri;
}createResizedImage(uri, 1200, 1200, "JPEG", 85) menurunkan resolusi maksimal 1200 piksel dan kualitas JPEG 85 persen — cukup untuk tampilan layar, ringan untuk jaringan.
Kelola permission lintas platform dengan react-native-permissions — satu API untuk iOS dan Android:
npm install react-native-permissionsimport { check, request, PERMISSIONS, RESULTS } from "react-native-permissions";
import { Platform } from "react-native";
const kamera =
Platform.OS === "ios" ? PERMISSIONS.IOS.CAMERA : PERMISSIONS.ANDROID.CAMERA;
async function pastikanIzin() {
const status = await check(kamera);
if (status === RESULTS.DENIED) {
return request(kamera);
}
return status;
}Platform.OS === "ios" memilih konstanta permission yang benar per platform. Urutan yang tepat: cek dulu, minta hanya jika berstatus DENIED, dan jangan minta ulang jika sudah BLOCKED — minta ulang di iOS yang sudah diblokir malah menolak tanpa dialog.
iOS wajib memiliki deskripsi penggunaan untuk setiap permission yang dipakai. Tanpa ini, aplikasi crash saat permission dipanggil:
NSCameraUsageDescription: Aplikasi memakai kamera untuk memotret foto profil.
NSPhotoLibraryUsageDescription: Aplikasi memakai galeri untuk memilih gambar.Teks deskripsi ini tampil di dialog permission iOS — tulis yang jelas dan jujur tentang kegunaan fitur.
User berhak menolak izin. Saat status BLOCKED, dialog tidak bisa dimunculkan lagi — satu-satunya jalan adalah pengaturan sistem. Tangani dengan menampilkan penjelasan dan tombol membuka pengaturan:
import { openSettings } from "react-native-permissions";
async function saatDiblokir() {
const izin = await pastikanIzin();
if (izin === RESULTS.BLOCKED) {
openSettings();
}
}openSettings() membuka layar pengaturan aplikasi di sistem. Kombinasi cek status, pesan penjelasan, dan jalan ke pengaturan membuat user paham kenapa fitur butuh izin dan bagaimana mengaktifkannya kembali.
Fitur kamera bukan segalanya. Saat izin ditolak, tampilkan alternatif: input manual, upload dari galeri, atau fitur lain yang tidak butuh izin. Aplikasi yang baik tetap berguna meski sebagian permission ditolak.
Warning
Jangan meminta banyak permission sekaligus saat startup. Sistem dan user akan curiga, dan level penolakan naik drastis. Minta permission pada konteks fitur yang relevan dan jelaskan manfaatnya sebelum dialog muncul.
Episode 12 menguasai media: kamera dengan VisionCamera, pemilihan dan pemrosesan gambar, permission lintas platform dengan react-native-permissions, deskripsi usage yang benar, serta pengalaman user saat izin ditolak.
Inti yang harus dibawa pulang:
isActive kamera saat layar tidak tampil untuk hemat baterai.DENIED, jangan minta ulang saat BLOCKED.Di episode 13 selanjutnya kita akan membahas secure storage dan credentials: Keychain dan Keystore untuk token, menghindari secret di bundle, konfigurasi .env per build, serta kebiasaan agar secret tidak pernah ter-commit.