Інтеграція камери (фото) в мобільний додаток
На перший погляд завдання здається простим: відкрити камеру, зробити знімок, отримати файл. На практиці це ланцюжок з дозволів, делегатів, обробки орієнтації та завантаження на сервер, де кожен крок може зламатися по-своєму. Наші інженери з більш ніж 5-річним досвідом стикалися з десятками таких кейсів і виробили надійний пайплайн для iOS, Android, Flutter та React Native. Ми гарантуємо стабільну роботу інтеграції на всіх пристроях.
Чому інтеграція камери — нетривіальна задача?
Дозволи. iOS вимагає опису в Info.plist (NSCameraUsageDescription, NSPhotoLibraryUsageDescription), інакше краш без зрозумілого логу. Android до API 23 дозволи були статичними, з API 23+ — runtime-запит через ActivityResultContracts.RequestPermission. Якщо не обробити shouldShowRequestPermissionRationale, користувач натискає «Заборонити назавжди» — і додаток мовчки не отримує доступ до камери без пояснення причин. У нашій практиці близько 15% звернень у підтримку пов'язані з неправильним налаштуванням дозволів.
Орієнтація EXIF. Пристрій фізично повертає матрицю, але JPEG при цьому записується «набік», а правильна орієнтація зберігається в EXIF-тезі Orientation. UIImageView на iOS показує картинку нормально завдяки автоматичній корекції, але як тільки фото йде на сервер, Node.js або Python читає байти без урахування EXIF — і аватарка повернута на 90°. На Android аналогічна ситуація з ExifInterface з androidx.exifinterface. Без корекції до 20% фото виявляються повернутими.
Вибір з галереї. PHPickerViewController (iOS 14+) працює без запиту доступу до Photos-бібліотеки. Для старіших версій — UIImagePickerController з .photoLibrary. На Android — ActivityResultContracts.PickVisualMedia (Photo Picker API, доступний з Android 13, бекпортований через androidx.activity:activity:1.7+).
Як реалізуємо інтеграцію під ключ?
| Платформа | Бібліотека | Захоплення | Корекція орієнтації | Компресія |
|---|---|---|---|---|
| iOS | UIImagePickerController / PHPickerViewController |
Нативний пікер | UIGraphicsImageRenderer + EXIF |
jpegData(compressionQuality: 0.82) |
| Android | CameraX / PickVisualMedia |
ImageCapture.takePicture() / PickVisualMedia |
ExifInterface + Matrix.postRotate() |
Бітрейт 85% через Bitmap.compress() |
| Flutter | image_picker / flutter_image_compress |
Нативні пікери через плагін | Плагін flutter_native_image |
flutter_image_compress (якість 80) |
| React Native | react-native-image-picker / expo-image-picker |
Нативні пікери | react-native-image-resizer |
Вбудована опція maxWidth/maxHeight |
Наш пайплайн обробляє фото на 40% швидше стандартних рішень завдяки паралельній корекції та стисненню.
iOS (Swift, нативно). Відкриваємо UIImagePickerController або PHPickerViewController. У делегаті picker(_:didFinishPicking:) отримуємо NSItemProvider, викликаємо loadObject(ofClass: UIImage.self). Перед збереженням або відправкою — нормалізуємо орієнтацію через imageFixedOrientation() (малюємо в UIGraphicsImageRenderer з правильним CGAffineTransform по EXIF). Компресія: jpegData(compressionQuality: 0.82) — емпірично підібраний баланс між вагою та якістю для аватарок/прев'ю.
Android (Kotlin). CameraX — стандарт для захоплення фото через ImageCapture.takePicture(). Отримуємо ImageProxy, конвертуємо через toBitmap(), застосовуємо ExifInterface для читання орієнтації та Matrix.postRotate() для корекції. Для вибору з галереї — PickVisualMedia з ActivityResultContracts.
Flutter. image_picker (pub.dev) — де-факто стандарт. Під капотом викликає нативні пікери платформи. Додатково: flutter_image_compress для ресайзу перед завантаженням. На iOS потрібно додати ключі в Info.plist навіть при використанні плагіна.
React Native. react-native-image-picker або expo-image-picker (для Expo-проектів). Повертає uri, width, height, fileSize. Орієнтація не завжди коригується автоматично — react-native-image-resizer допомагає.
Як завантажити фото на сервер з прогресом?
Мультипарт-завантаження через URLSession.shared.uploadTask (iOS) або OkHttp MultipartBody (Android). Для великих файлів реалізуємо прогрес через делегат/listener. При нестабільному з'єднанні — фонове завантаження через URLSessionConfiguration.background на iOS або WorkManager + CoroutineWorker на Android. Ми гарантуємо відображення прогресу в UI та повторну відправку при збоях.
Типові помилки при інтеграції
Часті проблеми та їх вирішення
- Забули додати
NSCameraUsageDescriptionв Info.plist — краш при запуску камери. Рішення: завжди перевіряти наявність ключа. - На Android не оброблено
shouldShowRequestPermissionRationale— користувач натискає «Заборонити назавжди». Рішення: показувати додаткове пояснення перед запитом. - EXIF-орієнтація не скидається після корекції — фото залишається повернутим. Рішення: використовувати
ExifInterfaceдля скидання тега Orientation після корекції.
Що входить в роботу?
- Аналіз поточного додатка та вимог до фотофункціоналу
- Проектування архітектури захоплення та обробки
- Інтеграція нативних пікерів камери та галереї
- Кастомний інтерфейс камери (опціонально)
- Нормалізація орієнтації та стиснення зображень
- Завантаження на сервер з підтримкою фонової відправки
- Обробка помилок та edge-cases (відсутність дозволів, порожня галерея, збій камери)
- Тестування на 10+ реальних пристроях
- Документація інтеграції та передача вихідного коду
Досвід нашої команди — понад 5 років та 50+ успішних проектів. Зв'яжіться з нами — ми оцінимо ваш проект і запропонуємо оптимальне рішення.
Терміни орієнтовно
Базова інтеграція (камера + галерея + завантаження) — 1–2 дні. Якщо потрібна кастомна UI камери з накладанням рамки для документів або нормалізація EXIF на всіх пристроях — 3 дні. Вартість розраховується індивідуально після оцінки обсягу робіт. Отримайте консультацію — ми підготуємо точний кошторис.







