Проблемы доступа к библиотеке фотографий iOS в многоплатформенном приложении ComposeIOS

Программируем под IOS
Ответить
Anonymous
 Проблемы доступа к библиотеке фотографий iOS в многоплатформенном приложении Compose

Сообщение Anonymous »

Я разрабатываю мультиплатформенное приложение Compose, которое корректно работает на Android, но сталкивается с проблемами при доступе к библиотеке фотографий на iOS. Приложение использует ImagePicker, позволяющее пользователям выбирать изображения, и реализует обработку разрешений для доступа к библиотеке фотографий.
В журнале ошибок показано:
[PAAccessLogger] Failed to log access with error: access=
accessor: identifier:0284C142-B9D2-4134-99B0-337308EC891E kind:intervalEnd timestampAdjustment:0 tccService:kTCCServicePhotos, error=Error Domain=PAErrorDomain Code=10 "Possibly incomplete access interval automatically ended by daemon"
[core] "Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)""

Эта ошибка предполагает, что могут быть проблемы с тем, как приложение обрабатывает разрешения на доступ к библиотеке фотографий или как оно взаимодействует с API библиотеки фотографий iOS.
Я реализовал запросы разрешений. и функциональность выбора изображений в моем проекте Compose Multiplatform с отдельными реализациями для iOS в исходном наборе iosMain. Несмотря на эти реализации, приложение по-прежнему сталкивается с ошибками при попытке доступа к библиотеке фотографий на устройствах iOS.
Я ищу информацию о том, что может быть причиной этой проблемы, специфичной для iOS, и как ее решить, сохраняя при этом перекрестный доступ. совместимость платформ в моем проекте Compose Multiplatform.
CommonMain:
@Composable
fun PostScreen(onEventSubmitted: () -> Unit) {
val viewModel: PostsViewModel = KoinPlatform.getKoin().get()
val imagePicker: ImagePicker = KoinPlatform.getKoin().get()
val cameraPermissionManager = remember { RequestPhotoLibraryPermission() }
val uiState by viewModel.uiState.collectAsState()

var selectedImage by remember { mutableStateOf(null) }
rememberCoroutineScope()

if (uiState.pickImage) {
imagePicker.PickImage { imageBytes ->
viewModel.setEvent(PostsEvents.OnPickImage(false))
selectedImage = imageBytes
}

}

cameraPermissionManager.RequestCameraPermission(
onPermissionGranted = {
AddPostEvent(
selectedImage = selectedImage,
onEventSubmitted = onEventSubmitted,
setEvent = { event ->
viewModel.setEvent(event)
})
},
onPermissionDenied = {
BodyMedium(text = "La fotocamera non è disponibile senza permessi")
})

LaunchedEffect(viewModel) {
viewModel.effect.collect { effect ->
when (effect) {
is PostsUIEffects.ShowSuccess -> {
println("XXXXX success")
}

is PostsUIEffects.ShowErrorMessage -> {
println("XXXXX error: ${effect.message}")
}
}
}
}
}

iosMain:
запрос разрешения
actual class RequestPhotoLibraryPermission {
@Composable
actual fun RequestCameraPermission(
onPermissionGranted: @Composable () -> Unit,
onPermissionDenied: @Composable () -> Unit
) {
var permissionStatus by remember { mutableStateOf(PHAuthorizationStatusNotDetermined) }

LaunchedEffect(Unit) {
permissionStatus = PHPhotoLibrary.authorizationStatus()
if (permissionStatus == PHAuthorizationStatusNotDetermined) {
permissionStatus = requestPhotoLibraryPermission()
}
}

when (permissionStatus) {
PHAuthorizationStatusAuthorized,
PHAuthorizationStatusLimited -> onPermissionGranted()

else -> onPermissionDenied()
}
}

private suspend fun requestPhotoLibraryPermission(): PHAuthorizationStatus =
suspendCancellableCoroutine { continuation ->
PHPhotoLibrary.requestAuthorization { status ->
continuation.resume(status)
}
}
}

iosMain ImagePicker:
actual class ImagePicker {
private var onImagePickedCallback: ((ByteArray?) -> Unit)? = null
private val pickerController = UIImagePickerController()

init {
pickerController.sourceType = UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypePhotoLibrary
pickerController.setDelegate(object : NSObject(), UIImagePickerControllerDelegateProtocol, UINavigationControllerDelegateProtocol {
override fun imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo: Map) {
val image = didFinishPickingMediaWithInfo[UIImagePickerControllerOriginalImage] as? UIImage
picker.dismissViewControllerAnimated(true) {
if (image != null) {
val data = UIImageJPEGRepresentation(image, 0.8)
val bytes = data?.toByteArray()
onImagePickedCallback?.invoke(bytes)
} else {
onImagePickedCallback?.invoke(null)
}
onImagePickedCallback = null
}
}

override fun imagePickerControllerDidCancel(picker: UIImagePickerController) {
picker.dismissViewControllerAnimated(true) {
onImagePickedCallback?.invoke(null)
onImagePickedCallback = null
}
}
})
}

@Composable
actual fun PickImage(onImagePicked: (ByteArray?) -> Unit) {
val scope = rememberCoroutineScope()

LaunchedEffect(Unit) {
scope.launch(Dispatchers.Main) {
showImagePicker(onImagePicked)
}
}
}

private suspend fun showImagePicker(onImagePicked: (ByteArray?) -> Unit) = suspendCancellableCoroutine { continuation ->
onImagePickedCallback = { bytes ->
onImagePicked(bytes)
continuation.resume(Unit)
}

UIApplication.sharedApplication.keyWindow?.rootViewController?.let { rootViewController ->
rootViewController.presentViewController(pickerController, animated = true, completion = null)
} ?: run {
onImagePickedCallback?.invoke(null)
continuation.resume(Unit)
}
}
}

@OptIn(ExperimentalForeignApi::class)
fun NSData.toByteArray(): ByteArray = ByteArray(this.length.toInt()).apply {
usePinned {
memcpy(it.addressOf(0), this@toByteArray.bytes, this@toByteArray.length)
}
}


Подробнее здесь: https://stackoverflow.com/questions/789 ... atform-app
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «IOS»