Lesson 1 of 15
Using native features
A production app usually needs the device's native features — the camera, location, contacts, biometrics, the file system, sensors. These are what make a mobile app feel like a real app rather than a website. This lesson covers accessing native features from a cross-platform (React Native / Expo) app, and doing it safely and gracefully — because native features come with permissions, platform differences, and failure modes you must handle in production. Let's tap the device. Let's use native features well.
How native features work in a cross-platform app
NATIVE FEATURES = device capabilities exposed through libraries/modules:
WITH EXPO (the easy path) — a rich set of ready-made modules:
import * as ImagePicker from 'expo-image-picker';
import * as Location from 'expo-location';
// Camera / photo library:
const res = await ImagePicker.launchCameraAsync({ quality: 0.7 });
if (!res.canceled) setPhoto(res.assets[0].uri);
// Location:
const { status } = await Location.requestForegroundPermissionsAsync();
if (status === 'granted') {
const pos = await Location.getCurrentPositionAsync();
}
COMMON NATIVE FEATURES: camera + photos, location/GPS, contacts, calendar,
notifications, biometrics (Face/Touch ID), file system, sensors (accelerometer),
haptics, sharing, clipboard, secure storage, Bluetooth.
TWO WORLDS:
- EXPO modules (expo-camera, expo-location, expo-notifications, ...) cover most
needs with one JS API across both platforms. Use a DEV/EAS build for modules
Expo Go can't include.
- BARE / custom NATIVE MODULES — write/native-link platform code (Kotlin/Swift)
when no library exists. More work; the escape hatch for anything.
Native features are the device's capabilities — camera, location, contacts, biometrics, sensors, file system — exposed to your JavaScript through libraries/modules. With Expo, there's a rich set of ready-made modules (expo-camera, expo-location, expo-notifications, expo-local-authentication, and many more) that give you one JS API across both platforms — you await an async call and get a result (a photo uri, a GPS position). Most features you'll need already exist as a module; for ones that need capabilities Expo Go can't include, you build a dev/EAS build. Beyond Expo lies the bare / custom native module world — writing or linking platform code (Kotlin/Swift) when no library exists — more work, but the escape hatch for anything the device can do. The mental model for production is: reach for a well-maintained module first, use a dev build when needed, and drop to native code only as a last resort. That keeps most of your app in one cross-platform codebase while still using the device fully.
Using native features safely in production
NATIVE FEATURES COME WITH RESPONSIBILITY — handle these EVERY time:
1. PERMISSIONS (next lesson) — camera/location/contacts/etc. require the user's
permission. REQUEST it (with context), and HANDLE DENIAL gracefully (don't
crash; degrade or explain). Never assume it's granted.
2. AVAILABILITY / FAILURE — a feature may be unavailable (no camera, GPS off,
sensor missing) or fail. CHECK availability + wrap calls in try/catch; show a
fallback. Never assume the hardware is there or the call succeeds.
3. PLATFORM DIFFERENCES — features + behaviour differ across iOS/Android (and OS
versions). Test BOTH; use Platform checks where needed.
4. PRIVACY + STORE RULES — accessing sensitive data (location, contacts, photos,
mic) requires a clear PURPOSE STRING (why you need it) in the config; stores
REJECT apps that over-ask or don't explain. Only request what you actually use.
5. REAL-DEVICE TESTING — native features are FAKED/limited in emulators. You MUST
test camera/GPS/biometrics/notifications on REAL devices, both platforms.
THE PRINCIPLE: use native features to make the app powerful + native-feeling — but
always request permission, handle denial + failure, respect privacy, and test on
real devices.
Native features come with responsibility — in production you handle five things every time. Permissions: camera, location, contacts, and the like require the user's permission, so you request it (with context) and handle denial gracefully — never assume it's granted (the next lesson goes deep). Availability and failure: a feature may be unavailable (no camera, GPS off) or fail, so check availability and wrap calls in try/catch with a fallback — never assume the hardware is there or the call succeeds. Platform differences: features and behaviour differ across iOS/Android and OS versions — test both, use Platform checks where needed. Privacy + store rules: accessing sensitive data needs a clear purpose string (why you need it) in the config, and stores reject apps that over-ask or don't explain — so only request what you actually use. And real-device testing: native features are faked/limited in emulators, so you must test camera/GPS/biometrics/notifications on real devices, both platforms. The principle: use native features to make the app powerful and native-feeling — but always request permission, handle denial and failure, respect privacy, and test on real devices.
The mistake beginners make
The first mistake is assuming permission is granted (or the feature works) — calling the camera/location without requesting permission or checking the result, so the app crashes or silently fails when the user hasn't granted it or the hardware isn't there; always request and handle denial/failure. The second mistake is not handling failure/availability — assuming every device has the feature and every call succeeds, with no try/catch or fallback; check availability and catch errors. The third mistake is over-requesting / missing purpose strings — asking for permissions you don't use or omitting the why string, which erodes trust and gets the app rejected; request only what you use, with a clear purpose. And only testing in the emulator — where native features are faked, so real bugs ship; test on real devices. Request + handle permissions, handle failure/availability, request only what you use (with purpose strings), and test native features on real devices.
Your turn
Your turn
- Use a native feature via a module: with Expo, add the camera/photo picker (expo-image-picker) or location (expo-location) — request permission, then use the feature and handle the result (e.g. show the photo / position).
- Handle denial + failure: wrap the native call in try/catch and check the permission status, showing a graceful fallback or explanation if it's denied or the feature is unavailable (GPS off, no camera) — never crash.
- Request only what you use: list the native features your app actually needs and remove any permissions/modules you don't — and add a clear purpose string (why) for each sensitive one in the config.
- Handle platform differences: test the feature on both iOS and Android and add Platform checks if behaviour differs; use a dev/EAS build if the module can't run in Expo Go.
- Test on a real device: run the native feature (camera/GPS/biometrics/notifications) on a real phone — since emulators fake these — and confirm it works on both platforms.
Key points
- Native features (camera, location, contacts, biometrics, sensors, file system) are exposed through libraries/modules — with Expo, ready-made modules (expo-camera/location/notifications/...) give one JS API across both platforms (use a dev/EAS build for modules Expo Go can't include); bare/custom native modules are the escape hatch for anything with no library.
- Reach for a well-maintained module first, a dev build when needed, and native code only as a last resort — keeping most of the app in one cross-platform codebase while using the device fully.
- Always: request PERMISSION (with context) + handle denial gracefully; check AVAILABILITY + wrap calls in try/catch with a fallback (feature may be missing or fail); handle PLATFORM DIFFERENCES (test both); respect PRIVACY (purpose strings, only request what you use — stores reject over-asking); and TEST on real devices (features are faked in emulators).
- Common native features: camera + photos, location/GPS, contacts, calendar, notifications, biometrics (Face/Touch ID), file system, sensors, haptics, sharing, clipboard, secure storage, Bluetooth.
- The mistakes: assuming permission is granted / the feature works (crashes or silently fails), not handling failure/availability (no try/catch or fallback), over-requesting / missing purpose strings (erodes trust + rejected), and only testing in the emulator (native features are faked).
Q&A · 0
Enrol to ask questions and join the discussion.
No questions yet — be the first to ask.