Chapter 17: Device APIs – Camera, Image Picker & Location
In previous chapters, we learned:
- Navigation
- Context API
- AsyncStorage
- API Integration
- Async Operations
Modern mobile applications also interact with device hardware and native capabilities.
Examples:
- Banking apps scan cheques using camera
- Food delivery apps use GPS location
- Expense apps attach receipt images
React Native applications can access these features using Expo APIs.
In this chapter, we will integrate:
✅ Camera API ✅ Image Picker API ✅ Location API ✅ Permission Handling
into our ExpenseApp.
🎯 What You Will Learn
By the end of this chapter, you will:
- Understand device permissions
- Use Image Picker API
- Use Camera API
- Use Location API
- Handle permission requests
- Work with file URIs
- Build receipt upload features
- Attach location data to expenses
- Handle async device operations safely
📱 Understanding Device Permissions
Mobile operating systems protect user privacy by requiring explicit permission before accessing sensitive features.
Examples:
- Camera
- Gallery
- GPS Location
- Contacts
- Microphone
🔹 Why Permissions Matter
Without permission handling:
❌ Application may crash ❌ Features may fail silently ❌ Poor user experience
With proper permission handling:
✅ Better UX ✅ Better security ✅ Reliable application behavior
🔹 Permission Flow
Application Requests Permission
↓
User Allows or Denies
↓
Application Handles Result
🔹 Permission States
| Status | Meaning |
|---|---|
| granted | Access allowed |
| denied | User rejected |
| undetermined | Not requested yet |
🔹 Best Practices
✅ Request permissions only when needed
✅ Explain why permission is required
✅ Handle denied permissions properly
✅ Always provide fallback behavior
🖼️ Image Picker API
Image Picker allows users to select images from the device gallery.
🔹 Install Dependency
npx expo install expo-image-picker
🔹 ExpenseApp Use Case
Users can:
- Select receipt images
- Attach bills to expenses
- Upload payment screenshots
🔹 Pick Image Example
📁 src/app/add-expense.tsx
import * as ImagePicker from "expo-image-picker";
const pickReceiptImage = async () => {
try {
const permission =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!permission.granted) {
alert("Gallery permission is required");
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
allowsEditing: true,
quality: 0.7,
});
if (!result.canceled) {
const imageUri = result.assets[0].uri;
console.log(imageUri);
}
} catch (error) {
console.log(error);
}
};
🔹 Important Options
| Option | Purpose |
|---|---|
| mediaTypes | Image/video selection |
| allowsEditing | Crop image |
| quality | Compression quality |
🔹 Why Compress Images?
Large images:
❌ Slow application ❌ Increase memory usage ❌ Increase upload time
Using:
quality: 0.7;
helps improve performance.
📷 Camera API
Camera API allows users to capture photos directly using device camera.
🔹 Install Camera Package
npx expo install expo-camera
🔹 Request Camera Permission
import { Camera } from "expo-camera";
const requestCameraPermission = async () => {
const { status } = await Camera.requestCameraPermissionsAsync();
if (status !== "granted") {
alert("Camera permission denied");
return;
}
};
🔹 Basic Camera Preview
<Camera
style={{
flex: 1,
}}
/>
🔹 Capture Photo
const takePicture = async () => {
if (!cameraRef.current) {
return;
}
const photo = await cameraRef.current.takePictureAsync();
console.log(photo.uri);
};
🔹 ExpenseApp Use Cases
Users can:
- Capture receipts instantly
- Scan bills
- Store invoice images
📍 Location API
Location API provides GPS coordinates.
🔹 Install Location Package
npx expo install expo-location
🔹 Get Current Location
import * as Location from "expo-location";
const getCurrentLocation = async () => {
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
alert("Location permission denied");
return;
}
const location = await Location.getCurrentPositionAsync({});
console.log(location.coords);
} catch (error) {
console.log(error);
}
};
🔹 Example Location Output
{
"latitude": 19.076,
"longitude": 72.8777
}
🔹 ExpenseApp Use Cases
Location can help:
- Track spending locations
- Generate city-wise reports
- Analyze travel expenses
📁 Working with File URIs
Image Picker and Camera APIs return file URIs.
🔹 Example URI
file:///data/user/0/.../receipt.jpg
🔹 Display Image
<Image
source={{ uri: receiptUri }}
style={{
width: 120,
height: 120,
}}
/>
🔹 Important Notes
✅ URI is file reference only
✅ Do not store large image blobs in state
✅ Upload images to server when required
🔄 ExpenseApp Full Example
📁 src/app/add-expense.tsx
import React, { useState } from "react";
import { View, Text, TextInput, Pressable, Image } from "react-native";
import * as ImagePicker from "expo-image-picker";
import * as Location from "expo-location";
import { style } from "../styles/styles";
export default function AddExpenseScreen() {
const [description, setDescription] = useState("");
const [amount, setAmount] = useState("");
const [receiptUri, setReceiptUri] = useState("");
const [location, setLocation] = useState<any>(null);
const pickReceiptImage = async () => {
try {
const permission =
await ImagePicker.requestMediaLibraryPermissionsAsync();
if (!permission.granted) {
alert("Gallery permission required");
return;
}
const result = await ImagePicker.launchImageLibraryAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 0.7,
});
if (!result.canceled) {
setReceiptUri(result.assets[0].uri);
}
} catch (error) {
console.log(error);
}
};
const fetchLocation = async () => {
try {
const { status } =
await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
alert("Location permission denied");
return;
}
const currentLocation = await Location.getCurrentPositionAsync({});
setLocation(currentLocation.coords);
} catch (error) {
console.log(error);
}
};
const handleSaveExpense = async () => {
const expense = {
id: Date.now(),
description,
amount: Number(amount),
receiptUri,
location,
};
console.log(expense);
};
return (
<View style={styles.container}>
<Text style={styles.label}>Description</Text>
<TextInput
value={description}
onChangeText={setDescription}
placeholder="Description"
style={styles.input}
/>
<Text style={styles.label}>Amount</Text>
<TextInput
value={amount}
onChangeText={setAmount}
placeholder="Amount"
keyboardType="numeric"
style={styles.input}
/>
<Pressable style={styles.button} onPress={pickReceiptImage}>
<Text style={styles.buttonText}>Select Receipt</Text>
</Pressable>
{receiptUri ? (
<Image
source={{
uri: receiptUri,
}}
style={{
width: 120,
height: 120,
marginVertical: 20,
}}
/>
) : null}
<Pressable
style={[
styles.button,
{
marginTop: 10,
},
]}
onPress={fetchLocation}
>
<Text style={styles.buttonText}>Get Location</Text>
</Pressable>
<Pressable
style={[
styles.button,
{
marginTop: 10,
},
]}
onPress={handleSaveExpense}
>
<Text style={styles.buttonText}>Save Expense</Text>
</Pressable>
</View>
);
}
🔹 Async Flow in Device APIs
User Presses Button
↓
Permission Requested
↓
User Allows Access
↓
Device API Executes
↓
Result Returned
↓
UI Updates
⚠️ Error Handling
Always handle:
- Permission denial
- API failures
- Device incompatibility
- User cancellation
🔹 Example Error Handling
try {
const result = await ImagePicker.launchImageLibraryAsync();
} catch (error) {
console.log(error);
}
🔹 Recommended Folder Structure
src
│
├── app
│ └── add-expense.tsx
│
├── components
│ └── receipt-preview.tsx
│
├── hooks
│ └── use-location.ts
│
├── services
│ └── expenseService.ts
│
└── styles
└── styles.ts
⚡ Performance Considerations
✅ Compress large images ✅ Avoid unnecessary re-renders ✅ Avoid storing large binary data ✅ Use lazy image loading
🔐 Security Considerations
✅ Respect user privacy ✅ Request permissions carefully ✅ Avoid unnecessary location tracking ✅ Never access device APIs silently
✅ Best Practices
✅ Request permissions contextually ✅ Handle denied permissions gracefully ✅ Use async/await properly ✅ Keep UI responsive ✅ Reuse existing styles ✅ Separate business logic cleanly
⚠️ Common Mistakes
❌ Forgetting permission requests ❌ Ignoring denied permissions ❌ Blocking UI during async operations ❌ Storing large images in state ❌ No error handling ❌ Mixing too much logic in components
🧪 Practice Exercises
- Capture image using Camera API
- Add receipt preview component
- Store expense location
- Add loading indicator during image selection
- Create reusable permission utility
- Upload image to mock API
- Add delete receipt functionality
📘 Chapter Summary
In this chapter, you:
✅ Learned device permission handling ✅ Used Image Picker API ✅ Used Camera API ✅ Used Location API ✅ Worked with file URIs ✅ Attached receipt images to expenses ✅ Stored GPS location data ✅ Handled async device operations safely ✅ Built real-world ExpenseApp features