У меня не очень большой опыт разработки — я скорее «вайбер-программист» — и это обязательный проект, который мне нужно выполнить. Возможно, мне не хватает чего-то простого. Я попробовал несколько подходов и даже обратился к нескольким LLM (GPT, Sonnet и т. д.), но ни один из них не смог решить эту проблему.
Среда
- React Native: 0.72.x
- Expo SDK: 52.0.38
- Рабочий процесс: Перенесено с Expo Managed Workflow → Bare Workflow
- Пакеты:
expo-camera (CameraView + useCameraPermissions) - expo-router
[*]Цель: пользователи делают снимки → подтверждают/переснимают → передают URI изображения в глобальное состояние.
Что Я хочу сделать
- Запросить разрешение камеры, если оно не предоставлено.
- Показать камеру с помощью CameraView.
- Разрешить пользователям:
- Переключаться между передней и задней камерой.
- Сделать снимок (takePictureAsync).
- Подтвердите или сделайте снимок повторно.
- Передайте URI захваченной фотографии в глобальное состояние и перейдите к следующему экрану.
- В разработке сборка:
Работает нормально — камера показывает, изображение снято, URI возвращен.
- Выпустить APK:
Не работает при съемке изображения.
Запрос разрешения работает нормально. - Камера открывается.
- При съемке изображения просто не работает работаю.
TypeError: Cannot read property 'Type' of undefined
in TakePicture
Еще один вариант со старыми опциями:
The 2nd argument cannot be cast to type expo.modules.camera.PictureOptions
(received class com.facebook.react.bridge.ReadableNativeMap)
Что я пробовал
- Проверено разрешение камеры → работает нормально.
- Проверено cameraRef.current и cameraReady → верно при съемке.
- Проверил разные параметры takePictureAsync:
- { quality: 1, skipProcessing: true }
- { quality: 1, skipProcessing: true, base64: false }
- { quality: 1, skipProcessing: true, exif: false }
Та же ошибка.
- Проверено использование CameraType и Camera.Type → обновлен импорт.
- Добавлены журналы консоли → разрешение предоставлено, камера готова, ссылка существует.
- Работает в разработке, сбой только в выпуске APK.
- Пересобран APK, очищены кеши → все равно вылетает.
- Проверил документы Expo + проблемы с GitHub → некоторые упоминают минификацию / Hermes, вызывающие проблемы с ReadableNativeMap.
- Попробовал подходы CameraView и Camera → проблема сохраняется.
- Почему takePictureAsync() работает в разработке, но происходит сбой в APK-версии релиза после перехода на пустой рабочий процесс?
- Известна ли проблема с CameraView / takePictureAsync в Expo SDK 52 Android?
- Нужно ли передавать разные параметры для выпускных сборок?
- Есть ли какие-либо обходные пути, чтобы сделать снимок без сбоев в выпуске?
- Следует ли мне полностью переключиться обратно на «Камеру» вместо «CameraView»?
- Камера открывается → пользователь может сделать снимок → получить URI фотографии → продолжить без сбоев.
- Я «вибрационный программист» — возможно, мне не хватает чего-то простого.
- LLM, такие как GPT и Sonnet, не могут помогите.
- Разработка сборки работает нормально — только выпуск APK дает сбой.
- Переход от Expo Managed → Bare workflow может быть уместен.
import { View, Text, TouchableOpacity, Image, StyleSheet, Alert } from "react-native";
import { useRouter } from "expo-router";
import { CameraView, useCameraPermissions } from "expo-camera";
import { useState, useRef, useEffect } from "react";
import { useGlobalState } from "../../context/GlobalState";
import { useTheme } from "../../context/ThemeContext";
import Svg, { Path, G, ClipPath, Defs, Rect } from "react-native-svg";
const TakePicture = () => {
const router = useRouter();
const { setSelectedImage } = useGlobalState();
const { theme } = useTheme();
const styles = themedStyles(theme);
const [permission, requestPermission] = useCameraPermissions();
const [cameraReady, setCameraReady] = useState(false);
const [facing, setFacing] = useState("back");
const [capturedPhoto, setCapturedPhoto] = useState(null);
const [showCamera, setShowCamera] = useState(false);
const cameraRef = useRef(null);
useEffect(() => {
return () => {
// Cleanup if needed
cameraRef.current = null;
};
}, []);
// Ask permission & show camera
const handleTakePicture = async () => {
try {
if (!permission?.granted) {
const result = await requestPermission();
if (!result.granted) {
Alert.alert("Permission Required", "Camera access is needed to take photos.");
return;
}
}
setShowCamera(true);
} catch (error) {
Alert.alert("Error", "Failed to access camera. Please try again.");
}
};
// Capture photo
const takePicture = async () => {
if (!cameraReady || !cameraRef.current) return;
try {
const options = { quality: 1, skipProcessing: true, base64: false, exif: false };
const photo = await cameraRef.current.takePictureAsync(options);
if (photo?.uri) setCapturedPhoto(photo.uri);
} catch (error) {
Alert.alert("Error", "Unable to take picture.");
}
};
const confirmPicture = () => {
if (capturedPhoto) {
setSelectedImage(capturedPhoto);
router.push("../uploadItems/uploadLoading");
}
};
const retakePicture = () => setCapturedPhoto(null);
const goBack = () => {
if (showCamera && !capturedPhoto) setShowCamera(false);
else router.replace("../uploadItems/uploadAnItem");
};
// Camera screen
if (showCamera && permission?.granted) {
return (
{capturedPhoto ? (
) : (
←
setCameraReady(true)}
>
setFacing(facing === "back" ? "front" : "back")}
>
)}
);
}
// Main screen
return (
router.replace("../uploadItems/uploadAnItem")}>
Camera
Take a Picture
You can take outfit photos using your camera
Take Picture
);
};
// Styles
const themedStyles = (theme) =>
StyleSheet.create({
container: { flex: 1, backgroundColor: theme.background },
header: { flexDirection: "row", alignItems: "center", gap: 90, marginTop: 52, marginBottom: 55, paddingHorizontal: 23 },
headerText: { fontSize: 23, fontWeight: "bold", color: theme.primary },
headingContainer: { marginBottom: 20, paddingHorizontal: 23, gap: 4 },
heading: { fontSize: 33, fontWeight: "bold", color: theme.primary },
subheading: { fontSize: 15, fontWeight: "500", color: theme.subTextGrey },
buttons: { flex: 1, flexDirection: "column", gap: 15, marginHorizontal: 23, marginTop: 12 },
takePictureButton: { width: 328, height: 64, borderRadius: 5, alignItems: "center", justifyContent: "center", backgroundColor: theme.orangish, flexDirection: "row", gap: 10 },
takePictureButtonText: { fontSize: 19, fontWeight: "700", color: "white" },
fullScreen: { flex: 1, width: "100%", height: "100%" },
overlay: { position: "absolute", bottom: 40, width: "100%", flexDirection: "row", justifyContent: "center", alignItems: "center" },
captureBtn: { width: 80, height: 80, borderRadius: 40, backgroundColor: theme.orangish, justifyContent: "center", alignItems: "center" },
capture
Подробнее здесь: https://stackoverflow.com/questions/798 ... ast-to-pic
Мобильная версия