Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: [iOS] CameraPlugin.presentCameraPicker Crashes Application #1996

Closed
crossan007 opened this issue Jan 17, 2024 · 8 comments
Closed

bug: [iOS] CameraPlugin.presentCameraPicker Crashes Application #1996

crossan007 opened this issue Jan 17, 2024 · 8 comments

Comments

@crossan007
Copy link

crossan007 commented Jan 17, 2024

Bug Report

Plugin(s)

Camera

Capacitor Version

Latest Dependencies:

  @capacitor/cli: 5.6.0
  @capacitor/core: 5.6.0
  @capacitor/android: 5.6.0
  @capacitor/ios: 5.6.0

Installed Dependencies:

  @capacitor/cli: 5.6.0
  @capacitor/core: 5.6.0
  @capacitor/ios: 5.6.0
  @capacitor/android: 5.6.0

[success] Android looking great! 👌
[error] Xcode is not installed

I'm using Ionic AppFlow for iOS builds. Since I develop on a Windows machine, I can't install XCode locally

Platform(s)

  • iPhone Xs Max (iOS 17.2.1)
  • iPhone 13 Pro Max (iOS 17.2.1)
  • iPhone Mini (unknown iOS version)

Current Behavior

When calling Camera.getPhoto() from JavaScript, the application crashes before displaying the camera live view

 const options: ImageOptions = {
    quality: 100,
    resultType: CameraResultType.Base64,
    source: CameraSource.Camera
  };

   try {
    let photo = await Camera.getPhoto(options);
    ...

The application will not crash if source is CameraSource.Photos.
The application will crash if source is CameraSource.Prompt and the user selects Camera; however it will not crash if the user selects gallery

Watching the real-time iOS device logs using 3uTools (I'm developing on Windows, so I don't have access to Apple's walled-garden tools) shows a few interesting lines (this is not a complete log capture; I just filtered for interesting stuff):

Jan 16 14:21:11 Charless-iPhone App(CameraUI)[625] <Error>: Unknown chipRevisionID (0xffffffff)
Jan 16 14:21:11 Charless-iPhone kernel(Sandbox)[0] <Error>: Sandbox: App(625) deny(1) iokit-get-properties iokit-class:IOPlatformDevice property:chip-revision
Jan 16 14:21:11 Charless-iPhone App(libMobileGestalt.dylib)[625] <Notice>: Taking legacy CameraOffset_2D path
Jan 16 14:21:11 Charless-iPhone App(CameraUI)[625] <Notice>: Enabling call status monitoring immediately
Jan 16 14:21:11 Charless-iPhone App(CameraUI)[625] <Error>: Attempted to change to mode Portrait with an unsupported device (BackDual). Auto device for both positions unsupported, returning Auto device for same position anyway (BackAuto).
Jan 16 14:21:11 Charless-iPhone App(CameraUI)[625] <Error>: Attempted to change to mode Portrait with an unsupported device (BackDual). Auto device for both positions unsupported, returning Auto device for same position anyway (BackAuto).
:21:11 Charless-iPhone App(CameraUI)[625] <Error>: Attempted to change to mode Portrait with an unsupported device (BackAuto). Auto device for both positions unsupported, returning Auto device for same position anyway (BackAuto).
Jan 16 14:21:11 Charless-iPhone App(CameraUI)[625] <Error>: Attempted to change to mode Portrait with an unsupported device (BackDual). Auto device for both positions unsupported, returning Auto device for same position anyway (BackAuto).
Jan 16 14:21:11 Charless-iPhone App(CoreFoundation)[625] <Notice>: *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFConstantString containsObject:]: unrecognized selector sent to instance 0x1ee6d4e20'
*** First throw call stack:
(0x197e4269c 0x190103c80 0x197ed2fdc 0x197d87e08 0x197ec8950 0x1b4533d44 0x19fd49300 0x19fd4ab3c 0x1b44f0bc0 0x1b4532344 0x19fd49300 0x19fd4ab3c 0x1b44e2150 0x1b4553124 0x1b44d4830 0x1b44d4260 0x1b452bb70 0x1fe679df4 0x1fe4f4678 0x1fe679d68 0x1fe5b9218 0x1fe4f3fe0 0x1fe4f3ad0 0x1fe4f35f4 0x1fe4f3684 0x1fe4f3684 0x1fe4f3174 0x1afa20650 0x19fd476a8 0x19fd49300 0x19fd50894 0x19fd513c4 0x19fd5c004 0x19fd5b878 0x201d3b964 0x201d3ba04)

I modified CAPLog.swift (https://github.com/ionic-team/capacitor/blob/main/ios/Capacitor/Capacitor/CAPLog.swift) to write to the iOS system log so that I could insert my own log points.

I modified CameraPlugin.swift to include CAPLog statements at various points in the execution chain. I also added do/catch error handling to various places in the Swift file to attempt logging the uncaught exception.

I determined that the app crash occurs sometime after the call to bridge?.viewController?.present in

bridge?.viewController?.present(picker, animated: true, completion: nil)

The plugin seems to correctly request authorization - the permission request dialogs appear on the screen.

I have the necessary strings in my Info.plist file:

  • NSCameraUsageDescription
  • NSMicrophoneUsageDescription
  • NSPhotoLibraryAddUsageDescription
  • NSPhotoLibraryUsageDescription

The crash happens every single time I attempt to open the camera. None of the do/catch error handlers actually catch an error.

Expected Behavior

Calling getPhoto should not crash when opening the camera.

Code Reproduction

Other Technical Details

Additional Context

Possibly related bugs:

Related Apple Developer Documentation

@ionitron-bot ionitron-bot bot added the triage label Jan 17, 2024
@crossan007
Copy link
Author

By process of trial and error of comparing my app with the Code Reproduction sample, I noticed a discrepancy in the format of Info.plist

My file contained an entry for

<key>UIBackgroundModes</key>
<string>remote-notification</string>

And scouring my git history for modifications to this line, I realized that sometime along the way, it was originally committed as this, but later changed to remove the <array> node:

<key>UIBackgroundModes</key>
<array>
  <string>remote-notification</string>
</array>

Re-adding the <array> node to the Info.plist file resolved the issue with the call to const photo = await Camera.getPhoto({ crashing the application.

A very weird issue, with a somewhat unexplained resolution.

Maybe Capacitor framework could add some error handling to the code path which opens the camera (or the code path that moves the application to the background) so that this exception is surfaced in a more helpful way.

@markemer
Copy link
Member

That top one should have been rejected by apple: Apple's UIBackgroundModes Docs

Seems like it's crashing on the Apple side, but in a weird way. Do you have a project I can look at that has this crash?

@markemer markemer self-assigned this Jan 19, 2024
@crossan007
Copy link
Author

crossan007 commented Jan 19, 2024

I'll put together a code reproduction project for you soon.

Apple has been happily accepting our Info.plist with this malformed key since 2021, but it only recently started causing a runtime crash 😅😅

@dmorfav
Copy link

dmorfav commented Feb 22, 2024

I am experiencing the same issue. I updated an app that had the Cordova camera plugin to Capacitor 5.7. When I launch the app for the first time and access the camera, after taking a photo, the app closes. I immediately reopen it, repeat the same process, and everything works without any issues. This only happens in an iOS environment, and I have tested it on different devices and iOS versions.

@dmorfav
Copy link

dmorfav commented Feb 22, 2024

I have been researching possible solutions, and I have found one.

I have implemented this method.

All this in a iPad 7ª Gen with IOS 16.2.

public checkAndRequestCameraPermission = async (): Promise<boolean> => {
		const { photos, camera } = await Camera.checkPermissions();

		if (photos === 'granted' && camera === 'granted') {
			return true;
		}

		const permissionsToRequest: CameraPermissionType[] = [];

		if (photos !== 'granted') permissionsToRequest.push('photos');
		if (camera !== 'granted') permissionsToRequest.push('camera');

		if (permissionsToRequest.length > 0) {
			const { photos: newPhotos, camera: newCamera } = await Camera.requestPermissions({
				permissions: permissionsToRequest,
			});

			return newPhotos === 'granted' && newCamera === 'granted';
		}

		return false;
	};

Previously, I used to call it before using the camera, and I would encounter the error. I have moved its invocation to the app.component of my Ionic/Angular project, and then navigate to the functionality, and it doesn't fail.

I'm not sure if it's the optimal solution, but it has at least solved the problem for me.

@Ionitron
Copy link
Collaborator

This issue needs more information before it can be addressed.
In particular, the reporter needs to provide a minimal sample app that demonstrates the issue.
If no sample app is provided within 15 days, the issue will be closed.

Please see the Contributing Guide for how to create a Sample App.

Thanks!
Ionitron 💙

@Ionitron
Copy link
Collaborator

It looks like this issue didn't get the information it needed, so I'll close it for now. If I made a mistake, sorry! I am just a bot.

Have a great day!
Ionitron 💙

Copy link

ionitron-bot bot commented Mar 25, 2024

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of the plugin, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators Mar 25, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

5 participants