NativeScript / nativescript-camera

NativeScript plugin to empower using device camera.
Apache License 2.0
92 stars 47 forks source link

iOS - Camera not opening after giving permissions #210

Closed jorwoody closed 4 years ago

jorwoody commented 5 years ago

Which platform(s) does your issue occur on?

Please, provide the following version numbers that your issue occurs with:

Please, tell us how to recreate the issue in as much detail as possible.

I've created a service which calls the nativescript-camera plugin in order to get an ImageAsset which will be used for uploading to my service. Trying to call the plugin to take a picture on an iOS device (tested through app-live device cloud on BrowserStack) does not open Camera, and returns failure.

NOTES:

Is there any code involved?

Here is the relevant service code.

import { Injectable } from '@angular/core'
import * as NativeCamera from 'nativescript-camera'
import * as FileSystem from 'tns-core-modules/file-system'
import { ImageAsset } from 'tns-core-modules/image-asset/image-asset'
import { ImageSource } from 'tns-core-modules/image-source/image-source'
import { isAndroid } from 'tns-core-modules/ui/page/page'

import { ClaimAttachmentModel } from '~/app/core/models/uploaded-file.model'

@Injectable({
    providedIn: 'root',
})
export class NativePhotoService {
    constructor() {}

    // See https://market.nativescript.org/plugins/nativescript-camera for more info on plugin usage
    async takePhoto(fileName: string): Promise<ClaimAttachmentModel> {
        if (!NativeCamera.isAvailable) {
            throw new Error('Camera is not available.') // iOS simulator will always return false, as there is no camera hardware
        }
        const permissionGranted = await NativeCamera.requestPermissions()

        if (permissionGranted) {
            const asset: ImageAsset = await NativeCamera.takePicture()
            return this.buildClaimAttachment(asset, fileName)
        }
    }

    private async buildClaimAttachment(
        asset: ImageAsset,
        fileName?: string,
    ): Promise<ClaimAttachmentModel> {
        let path: string
        let imageSource = new ImageSource()
        try {
            imageSource = await imageSource.fromAsset(asset)
        } catch (e) {
            // imageSource cannot be instantiated from "broken" file
            throw Error('Asset was corrupt or invalid; imageSource could not be instantiated.')
        }

        if (isAndroid) {
            // on taking photo, asset.android is temp location which we can reference
            // on selecting photo, asset.android is the path to the file
            path = asset.android
        } else {
            // iOS returns PHAsset, so we need to save to file system to get a path for reference
            if (!fileName) {
                // on selecting photo, retrieve URI to determine fileName
                const uriRequestOptions = PHImageRequestOptions.alloc().init()
                uriRequestOptions.synchronous = true

                PHImageManager.defaultManager().requestImageDataForAssetOptionsResultHandler(
                    asset.ios,
                    uriRequestOptions,
                    (data, util, orientation, info) => {
                        const uri: string = info.objectForKey('PHImageFileURLKey').toString()
                        fileName = this.getFileNameFromPath(uri)
                    },
                )
            }

            const folder = FileSystem.knownFolders.temp()
            path = FileSystem.path.join(folder.path, fileName)
            const saved = imageSource.saveToFile(path, 'jpg')
            if (!saved) {
                throw Error('Failed to save image to device.')
            }
        }

        if (!fileName) {
            fileName = this.getFileNameFromPath(path)
        }

        const claimAttachment = {
            fileName,
            path,
            imageSource,
        }
        return claimAttachment
    }
}
VladimirAmiorkov commented 5 years ago

Hi,

I was not able to reproduce this issue in our demo-angular application. In our test app when you correctly chain the requestPermissions() Promise and its .then call the takePicture() it is correctly executed after like it is shown here.

Can you provide us with a runnable project that shows the issue you are reporting when creating a custom service that wraps those APIs, you can use our playground to create a project.

jorwoody commented 5 years ago

Thanks for the response @VladimirAmiorkov . I had implemented this plugin using TypeScript's async/await functionality... but it seems that for some reason this strategy does not work for iOS permissions request.

Before I attempted to create a runnable example project, I tried following the pattern used in the demo application (chaining promises/callbacks) and it worked. Strange, but it works that way so I'm going with it. Would be nice to get async/await functionality working for cleaner code, but c'est la vie!

VladimirAmiorkov commented 5 years ago

Indeed it is not usual to that this is not working for iOS only. If you can send me a runnable project I would be happy to review it and see if there is something that could be done to help out in this scenario.

jorwoody commented 5 years ago

@VladimirAmiorkov I have started working on an example project, so I will try and finish that when I have time in the coming weeks.

elena-p commented 4 years ago

closing due to inactivity