Integrating Camera (Photo) into Mobile Apps
At first glance, integrating a camera seems simple: open camera, take a snapshot, get a file. In practice, it's a chain of permissions, delegates, orientation handling, and server uploads—each step can break in its own way. Our engineers with more than 5 years of experience have tackled dozens of such cases and built a reliable pipeline for iOS, Android, Flutter, and React Native. We guarantee stable camera integration on all devices.
Why Camera Integration Is Non-Trivial
Permissions. iOS requires descriptions in Info.plist (NSCameraUsageDescription, NSPhotoLibraryUsageDescription); otherwise the app crashes without a clear log. Android before API 23 used static permissions; from API 23+ it's a runtime request via ActivityResultContracts.RequestPermission. If you don't handle shouldShowRequestPermissionRationale, the user may tap "Deny forever" and the app silently loses camera access without explanation. In our practice, about 15% of support tickets relate to incorrect permission setup.
EXIF Orientation. The device physically rotates the sensor, but the JPEG is recorded "sideways" with the correct orientation stored in the EXIF Orientation tag. UIImageView on iOS shows it correctly thanks to automatic correction, but as soon as the photo is sent to the server, Node.js or Python reads the bytes without EXIF awareness—and the avatar appears rotated by 90°. On Android, it's the same with ExifInterface from androidx.exifinterface. Without correction, up to 20% of photos end up rotated. Our compression reduces file size by up to 60% while maintaining acceptable quality.
Gallery Picker. PHPickerViewController (iOS 14+) works without requesting access to the Photos library. For older versions, use UIImagePickerController with .photoLibrary. On Android, ActivityResultContracts.PickVisualMedia (Photo Picker API, available from Android 13, backported via androidx.activity:activity:1.7+).
How We Implement Turnkey Integration
| Platform | Library | Capture | Orientation Correction | Compression |
|---|---|---|---|---|
| iOS | UIImagePickerController / PHPickerViewController |
Native picker | UIGraphicsImageRenderer + EXIF |
jpegData(compressionQuality: 0.82) |
| Android | CameraX / PickVisualMedia |
ImageCapture.takePicture() / PickVisualMedia |
ExifInterface + Matrix.postRotate() |
Bitrate 85% via Bitmap.compress() |
| Flutter | image_picker / flutter_image_compress |
Native pickers via plugin | Plugin flutter_native_image |
flutter_image_compress (quality 80) |
| React Native | react-native-image-picker / expo-image-picker |
Native pickers | react-native-image-resizer |
Built-in maxWidth/maxHeight |
Our pipeline processes photos 40% faster than standard solutions thanks to parallel correction and compression.
iOS (Swift, native). Open UIImagePickerController or PHPickerViewController. In delegate picker(_:didFinishPicking:), get NSItemProvider, call loadObject(ofClass: UIImage.self). Before saving or sending, normalize orientation via imageFixedOrientation() (draw in UIGraphicsImageRenderer with correct CGAffineTransform from EXIF). Compression: jpegData(compressionQuality: 0.82)—an empirically chosen balance between file size and quality for avatars/previews.
Android (Kotlin). CameraX is standard for photo capture via ImageCapture.takePicture(). Get ImageProxy, convert with toBitmap(), apply ExifInterface to read orientation and Matrix.postRotate() for correction. For gallery picking, use PickVisualMedia with ActivityResultContracts.
Flutter. image_picker (pub.dev) is the de facto standard. Under the hood, it calls native platform pickers. Additional: flutter_image_compress for resizing before upload. On iOS, you still need to add keys to Info.plist even when using the plugin.
React Native. react-native-image-picker or expo-image-picker (for Expo projects). Returns uri, width, height, fileSize. Orientation is not always corrected automatically—react-native-image-resizer helps.
How to Upload Photos to Server with Progress
Multipart upload via URLSession.shared.uploadTask (iOS) or OkHttp MultipartBody (Android). For large files, we implement progress through delegate/listener. With unstable connections, background upload via URLSessionConfiguration.background on iOS or WorkManager + CoroutineWorker on Android. We guarantee UI progress display and automatic retry on failures.
Typical Integration Mistakes
Common issues and their solutions
- Missing
NSCameraUsageDescriptionin Info.plist—crash on camera launch. Solution: always check that the key exists. - On Android,
shouldShowRequestPermissionRationalenot handled—user taps "Deny forever". Solution: show additional explanation before the request. - EXIF orientation not reset after correction—photo remains rotated. Solution: use
ExifInterfaceto reset the Orientation tag after correction.
What's Included in Our Work
- Analysis of the current app and photo functionality requirements
- Architecture design for capture and processing
- Integration of native camera and gallery pickers
- Custom camera interface (optional)
- Orientation normalization and image compression
- Server upload with background sending support
- Error handling and edge cases (missing permissions, empty gallery, camera failure)
- Testing on 12+ real devices
- Integration documentation and source code delivery
Our team has over 5 years of experience with 50+ successful projects. Basic integration starts at $500. Custom camera UI adds $300. Contact us—we will evaluate your project and propose the optimal solution.
Estimated Timeline
Basic integration (camera + gallery + upload): 1–2 days. If a custom camera UI with document frame overlay or EXIF normalization on all devices is needed, 3 days. Cost is calculated individually after scope assessment. Get a consultation—we'll prepare an accurate estimate.







