NativeScript / ios

NativeScript for iOS and visionOS using V8
https://docs.nativescript.org/guide/ios-marshalling
130 stars 33 forks source link

UI API called on a background thread: -[UIView setNeedsLayout] #129

Open JacquesBeets opened 2 years ago

JacquesBeets commented 2 years ago

Describe the bug: We managed to update our app from NS6 to NS8 but we are getting this crash after we accessed a user's photos/videos from their device on iOS. I included our code we use to retrieve these items from the device as It could also be a factor. Please let me know if there is any other information required.

Environment:

{
  "author": "www.firstview.co”,
  "dependencies": {
    "@nativescript-community/ui-lottie": "^4.1.2",
    "@nativescript/background-http": "^5.0.2",
    "@nativescript/core": "~8.1.0",
    "@nativescript/firebase": "^11.1.3",
    "@nativescript/local-notifications": "^5.0.3",
    "@nativescript/theme": "3.0.1",
    "@triniwiz/nativescript-socketio": "^5.0.1",
    "@triniwiz/nativescript-toasty": "^4.1.3",
    "@vue/devtools": "^5.3.4",
    "moment": "^2.24.0",
    "nativescript-android-utils": "^1.0.2",
    "nativescript-calendar": "~3.0.0",
    "nativescript-contacts": "^1.6.4",
    "nativescript-dna-deviceinfo": "~3.7.1",
    "nativescript-permissions": "~1.3.11",
    "nativescript-sentry": "~2.0.1",
    "nativescript-socketio": "~3.3.1",
    "nativescript-ui-gauge": "~8.0.0",
    "nativescript-ui-sidedrawer": "~10.0.1",
    "nativescript-vue": "~2.9.0",
    "nativescript-vue-devtools": "~1.5.1",
    "nativescript-webview-interface": "~1.4.4",
    "vuex": "~3.6.2"
  },
  "devDependencies": {
    "@babel/core": "^7.0.0",
    "@babel/preset-env": "^7.0.0",
    "@nativescript/android": "8.0.0",
    "@nativescript/ios": "8.1.0",
    "@nativescript/types": "~8.1.1",
    "@nativescript/webpack": "~5.0.0",
    "babel-loader": "^8.0.2",
    "nativescript-vue-template-compiler": "^2.9.0",
    "sass": "^1.39.2",
    "typescript": "~4.0.0",
    "vue-loader": "^15.4.0"
  },
  "main": "./app/main.js"
}

Console Log:

CoreAnimation: warning, deleted thread with uncommitted CATransaction; set CA_DEBUG_TRANSACTIONS=1 in environment to log backtraces, or set CA_ASSERT_MAIN_THREAD_TRANSACTIONS=1 to abort when an implicit transaction isn't created on a main thread.
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0   CoreAutoLayout                      0x000000019a4b8dfc 09D27E78-A4B2-32D4-AB19-31802F490355 + 19964
1   CoreAutoLayout                      0x000000019a4bba58 09D27E78-A4B2-32D4-AB19-31802F490355 + 31320
2   UIKitCore                           0x000000018560d088 3F83EF9A-7492-3FEC-A486-5FC2A1E1B092 + 1736840
3   UIKitCore                           0x00000001855e2ae8 3F83EF9A-7492-3FEC-A486-5FC2A1E1B092 + 1563368
4   QuartzCore                          0x0000000186c4cc4c 148C6302-3BF5-38DC-864C-E86D004BAFE9 + 248908
5   QuartzCore                          0x0000000186c3fd40 148C6302-3BF5-38DC-864C-E86D004BAFE9 + 195904
6   QuartzCore                          0x0000000186c534a4 148C6302-3BF5-38DC-864C-E86D004BAFE9 + 275620
7   QuartzCore                          0x0000000186c5c08c 148C6302-3BF5-38DC-864C-E86D004BAFE9 + 311436
8   QuartzCore                          0x0000000186caf348 148C6302-3BF5-38DC-864C-E86D004BAFE9 + 652104
9   libsystem_pthread.dylib             0x00000001dcb20ec4
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
*** First throw call stack:
(0x183191cac 0x19a200758 0x19a4b8edc 0x19a4bba58 0x18560d088 0x1855e2ae8 0x186c4cc4c 0x186c3fd40 0x186c534a4 0x186c5c08c 0x186caf348 0x1dcb20ec4 0x1dcb23d3c 0x1dcb1f370 0x1dcb1efc0 0x1dcb1eaa4)

Xcode Log:

Main Thread Checker: UI API called on a background thread: -[UIView setNeedsLayout]
PID: 4461, TID: 1122759, Thread name: (none), Queue name: com.apple.photos.accessCallbacks, QoS: 0
Backtrace:
4   NativeScript                        0x00000001025d0044 ffi_call_SYSV + 68
5   NativeScript                        0x00000001025cf1d8 ffi_call_int + 968
6   NativeScript                        0x0000000102571854 _ZN3tns7Interop20CallFunctionInternalERNS_10MethodCallE + 428
7   NativeScript                        0x00000001024db32c _ZN3tns12ArgConverter6InvokeEN2v85LocalINS1_7ContextEEEP10objc_classNS2_INS1_6ObjectEEERNS_6V8ArgsEPKNS_10MethodMetaEb + 780
8   NativeScript                        0x000000010252ebc8 _ZN3tns15MetadataBuilder12InvokeMethodEN2v85LocalINS1_7ContextEEEPKNS_10MethodMetaENS2_INS1_6ObjectEEERNS_6V8ArgsENSt3__112basic_stringIcNSC_11char_traitsIcEENSC_9allocatorIcEEEEb + 88
9   NativeScript                        0x000000010252e454 _ZN3tns15MetadataBuilder14MethodCallbackERKN2v820FunctionCallbackInfoINS1_5ValueEEE + 220
10  NativeScript                        0x0000000102663e60 _ZN2v88internal25FunctionCallbackArguments4CallENS0_15CallHandlerInfoE + 544
11  NativeScript                        0x0000000102663360 _ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE + 524
12  NativeScript                        0x0000000102662af8 _ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE + 228
13  NativeScript                        0x0000000102d3e3cc Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 108
14  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
15  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
16  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
17  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
18  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
19  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
20  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
21  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
22  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
23  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
24  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
25  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
26  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
27  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
28  NativeScript                        0x0000000102cd536c Builtins_JSEntryTrampoline + 172
29  NativeScript                        0x0000000102cd5004 Builtins_JSEntry + 164
30  NativeScript                        0x00000001027afa70 _ZN2v88internal12_GLOBAL__N_16InvokeEPNS0_7IsolateERKNS1_12InvokeParamsE + 2532
31  NativeScript                        0x00000001027af058 _ZN2v88internal9Execution4CallEPNS0_7IsolateENS0_6HandleINS0_6ObjectEEES6_iPS6_ + 216
32  NativeScript                        0x000000010293ad14 _ZN2v88internal6Object23SetPropertyWithAccessorEPNS0_14LookupIteratorENS0_6HandleIS1_EENS_5MaybeINS0_11ShouldThrowEEE + 864
33  NativeScript                        0x000000010293e804 _ZN2v88internal6Object19SetPropertyInternalEPNS0_14LookupIteratorENS0_6HandleIS1_EENS_5MaybeINS0_11ShouldThrowEEENS0_11StoreOriginEPb + 420
34  NativeScript                        0x000000010293e584 _ZN2v88internal6Object11SetPropertyEPNS0_14LookupIt2021-10-13 10:27:21.810471+0200 mobilesafenative[4461:1122759] [reports] Main Thread Checker: UI API called on a background thread: -[UIView setNeedsLayout]
PID: 4461, TID: 1122759, Thread name: (none), Queue name: com.apple.photos.accessCallbacks, QoS: 0
Backtrace:
4   NativeScript                        0x00000001025d0044 ffi_call_SYSV + 68
5   NativeScript                        0x00000001025cf1d8 ffi_call_int + 968
6   NativeScript                        0x0000000102571854 _ZN3tns7Interop20CallFunctionInternalERNS_10MethodCallE + 428
7   NativeScript                        0x00000001024db32c _ZN3tns12ArgConverter6InvokeEN2v85LocalINS1_7ContextEEEP10objc_classNS2_INS1_6ObjectEEERNS_6V8ArgsEPKNS_10MethodMetaEb + 780
8   NativeScript                        0x000000010252ebc8 _ZN3tns15MetadataBuilder12InvokeMethodEN2v85LocalINS1_7ContextEEEPKNS_10MethodMetaENS2_INS1_6ObjectEEERNS_6V8ArgsENSt3__112basic_stringIcNSC_11char_traitsIcEENSC_9allocatorIcEEEEb + 88
9   NativeScript                        0x000000010252e454 _ZN3tns15MetadataBuilder14MethodCallbackERKN2v820FunctionCallbackInfoINS1_5ValueEEE + 220
10  NativeScript                        0x0000000102663e60 _ZN2v88internal25FunctionCallbackArguments4CallENS0_15CallHandlerInfoE + 544
11  NativeScript                        0x0000000102663360 _ZN2v88internal12_GLOBAL__N_119HandleApiCallHelperILb0EEENS0_11MaybeHandleINS0_6ObjectEEEPNS0_7IsolateENS0_6HandleINS0_10HeapObjectEEESA_NS8_INS0_20FunctionTemplateInfoEEENS8_IS4_EENS0_16BuiltinArgumentsE + 524
12  NativeScript                        0x0000000102662af8 _ZN2v88internalL26Builtin_Impl_HandleApiCallENS0_16BuiltinArgumentsEPNS0_7IsolateE + 228
13  NativeScript                        0x0000000102d3e3cc Builtins_CEntry_Return1_DontSaveFPRegs_ArgvOnStack_BuiltinExit + 108
14  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
15  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
16  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
17  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
18  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
19  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
20  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
21  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
22  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
23  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
24  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
25  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
26  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
27  NativeScript                        0x0000000102cd7598 Builtins_InterpreterEntryTrampoline + 248
28  NativeScript                        0x0000000102cd536c Builtins_JSEntryTrampoline + 172
29  NativeScript                        0x0000000102cd5004 Builtins_JSEntry + 164
30  NativeScript                        0x00000001027afa70 _ZN2v88internal12_GLOBAL__N_16InvokeEPNS0_7IsolateERKNS1_12InvokeParamsE + 2532
31  NativeScript                        0x00000001027af058 _ZN2v88internal9Execution4CallEPNS0_7IsolateENS0_6HandleINS0_6ObjectEEES6_iPS6_ + 216
32  NativeScript                        0x000000010293ad14 _ZN2v88internal6Object23SetPropertyWithAccessorEPNS0_14LookupIteratorENS0_6HandleIS1_EENS_5MaybeINS0_11ShouldThrowEEE + 864
33  NativeScript                        0x000000010293e804 _ZN2v88internal6Object19SetPropertyInternalEPNS0_14LookupIteratorENS0_6HandleIS1_EENS_5MaybeINS0_11ShouldThrowEEENS0_11StoreOriginEPb + 420
34  NativeScript                        0x000000010293e584 _ZN2v88internal6Object11SetPropertyEPNS0_14LookupIt

Our Service Fetching Images/Photos From the Device:

import { Device } from '@nativescript/core'
import { Sentry } from 'nativescript-sentry';

let osVersion = Device.osVersion;

@NativeClass
export class IndexMediaService {
    constructor() {

    }

    /**
     * Fetch media from device
     *
     * @param mediaTypes
     * @returns array
     */
    fetchMedia(mediaTypes) {

        let getPatternFromDes = function (des, pattern, ret=0) {
            let patternUrlMatch = des.match(pattern);
            return (patternUrlMatch !== null) ? patternUrlMatch[ret]: '';
        }

        let fetchOptions:PHFetchOptions = PHFetchOptions.alloc().init();
        let sortDescriptors = NSArray.arrayWithObject(
            NSSortDescriptor.sortDescriptorWithKeyAscending("creationDate", false)    
        );
        fetchOptions.sortDescriptors = sortDescriptors;
        fetchOptions.predicate = NSPredicate.predicateWithFormatArgumentArray(
            "mediaType = %d", NSArray.arrayWithObject(mediaTypes)
        );
        let fetchResult:PHFetchResult<PHAsset> = PHAsset.fetchAssetsWithOptions(fetchOptions);

        let mediaOnDevice = [];
        let mediaNotOnDevice = [];

        let mediaCnt = 0;

        fetchResult.enumerateObjectsUsingBlock((asset:PHAsset, index, stop) => {

            let media = {
                type: '',
                mimeType: '',
                sourceType: this.getSourceType(asset.sourceType),
                mediaSubTypes: this.getSubType(asset.mediaSubtypes),
                playbackStyle: this.getPlaybackStyle(asset.playbackStyle),
                uti: '',
                displayName: '',
                id: '',
                locallyAvailable: 'NO',
                imageUri: '',
                videoUri: '',
                width: 0,
                height: 0,
                size: 0,
                analysisType: '',
                cplResourceType: '',
                isCurrent: 'NO',
                dateTaken: asset.creationDate,
                dateAdded: asset.creationDate,
                dateModified: asset.modificationDate,
                orientation: '',
                backedUp: false
            };

            let assetResources = PHAssetResource.assetResourcesForAsset(asset);
            assetResources.enumerateObjectsUsingBlock((assetResource, ind1, stp1) => {                
                let type = this.getMediaType(assetResource.type);
                let des = assetResource.debugDescription;

                try{
                    if (parseInt(osVersion) >= 13) {
                        media.type = getPatternFromDes(des, "type:.*").replace("type: ", "");
                        media.uti = getPatternFromDes(des, "uti:.*").replace("uti: ", "");
                        media.locallyAvailable = getPatternFromDes(des, "locallyAvailable:.*").replace("locallyAvailable: ", "");
                        media.imageUri = getPatternFromDes(des, "file:.*").replace("file://", "");
                        media.videoUri = getPatternFromDes(des, "file:.*").replace("file://", "");
                        media.width = getPatternFromDes(des, "width:.*").replace("width: ", "");
                        media.height = getPatternFromDes(des, "height:.*").replace("height: ", "");
                        media.analysisType = getPatternFromDes(des, "analysisType:.*").replace("analysisType: ", "");
                        media.cplResourceType = getPatternFromDes(des, "cplResourceType:.*").replace("cplResourceType: ", "");
                        media.isCurrent = getPatternFromDes(des, "isCurrent:.*").replace("isCurrent: ", "");
                    } else if (parseInt(osVersion) >= 12 && parseInt(osVersion) < 13) {
                        media.type = getPatternFromDes(des, "type=(.*) size", 1);
                        media.uti = getPatternFromDes(des, "uti=(.*) filename", 1);                   
                    } else if (parseInt(osVersion) >= 11 && parseInt(osVersion) < 12) {
                        media.type = getPatternFromDes(des, "type=(.*) size", 1);
                        media.uti = getPatternFromDes(des, "uti=(.*) filename", 1); 
                    }
                    console.log("assetResource", des)
                    media.displayName = assetResource.originalFilename ? assetResource.originalFilename.toString() : getPatternFromDes(des, "filename:.*").replace("filename: ", "");
                    media.id = assetResource.assetLocalIdentifier ? assetResource.assetLocalIdentifier.toString() : getPatternFromDes(des, "asset:.*").replace("asset: ", "")
                    console.log("media.id", getPatternFromDes(des, "asset:.*").replace("asset: ", ""))
                    media.mimeType = this.getMimeType(media.displayName) ? this.getMimeType(media.displayName) : "noMimeType"
                    media.size = assetResource.valueForKey('fileSize') ? assetResource.valueForKey('fileSize') : "noFileSize"
                }catch(err){
                    let errorObj = {
                        errorMessage: err,
                        mediaObj: media
                    }
                    Sentry.captureException(err, {});
                    Sentry.captureMessage(JSON.stringify(errorObj))
                    throw errorObj
                }
            });

            if (media.type === 'photo' && media.imageUri === '' && media.locallyAvailable === 'YES') {
                const opt = PHImageRequestOptions.new();
                opt.version = PHImageRequestOptionsVersion.Current;

                PHImageManager.defaultManager().requestImageDataForAssetOptionsResultHandler(
                    asset,
                    opt,
                    (imageData, dataUTI, orientation, info) => {

                        try{
                            media.imageUri = info.objectForKey("PHImageFileURLKey").toString().replace("file://", "");
                        }
                        catch(err){
                            let errorObj = {
                                errorMessage: err,
                                mediaObj: media
                            }
                            console.error(errorObj)
                            Sentry.captureException(err, {});
                            Sentry.captureMessage(JSON.stringify(errorObj))
                            throw errorObj
                        }

                        if (media.imageUri !== '' && media.mimeType !== '') {
                            mediaOnDevice.push(media);
                        } else {
                            mediaNotOnDevice.push(media);
                        }
                        mediaCnt++;
                    }
                )
            } else if (media.type === 'video' && media.videoUri === '' && media.locallyAvailable === 'YES') {
                let opt: PHVideoRequestOptions;
                opt = PHVideoRequestOptions.new();
                opt.version = PHVideoRequestOptionsVersion.Original;

                PHImageManager.defaultManager().requestAVAssetForVideoOptionsResultHandler(
                   asset,
                   opt,
                   (avasset, audioMix, info) => {
                        let avurlAsset = avasset as AVURLAsset;
                        media.videoUri = avurlAsset.URL.toString().replace("file://", "");

                        if (media.videoUri !== '' && media.mimeType !== '') {
                            mediaOnDevice.push(media);
                        } else {
                            mediaNotOnDevice.push(media);
                        }
                        mediaCnt++;
                   }
                )
            } else {
                if (media.locallyAvailable === 'YES' && media.imageUri !== '' && media.mimeType !== '') {
                    mediaOnDevice.push(media);
                } else {
                    mediaNotOnDevice.push(media);
                }
                mediaCnt++;
            }
        })
        return mediaOnDevice;
    }

    /**
     * Get mime type
     * @param fileName
     * @returns string
     */
    getMimeType(fileName) {
        let extToMimes = {
            'jpg': 'image/jpeg',
            'jpeg': 'image/jpeg',
            'png': 'image/png',
            'heic': "image/heic",
            'mp4': "video/mp4",
            'mov': "video/quicktime",
        }
        let ext = fileName.toLowerCase().split('.').pop();
        if (extToMimes.hasOwnProperty(ext)) {
            return extToMimes[ext];
        }
        return '';
    }

    /**
     * Get media type
     * @param mediaId
     * @returns String
     */
    getMediaType(mediaId) {
        switch (mediaId) {
            case PHAssetMediaType.Audio:
                return 'Audio'
                break;
            case PHAssetMediaType.Image:
                return 'Image';
                break;
            case PHAssetMediaType.Video:
                return 'Video';
            default:
                return 'Unkown';
                break;
        }
    }

    /**
     * Get source type
     * @param sourceId
     * @returns string 
     */
    getSourceType(sourceId) {
        switch (sourceId) {
            case PHAssetSourceType.UserLibrary:
                return 'User Library';
                break;
            case PHAssetSourceType.iTunesSynced:
                return 'iTunes Synced'
                break;
            case PHAssetSourceType.CloudShared:
                return 'Cloud Shared'
                break;
            default:
                return 'None'
                break;
        }
    }

    /**
     * Get media sub types
     * @param subTypeId 
     * @returns string
     */
    getSubType(subTypeId) {
        switch (subTypeId) {
            case PHAssetMediaSubtype.PhotoPanorama:
                return 'Photo Panorama';
                break;
            case PHAssetMediaSubtype.PhotoHDR:
                return 'Photo HDR';
                break;
            case PHAssetMediaSubtype.PhotoScreenshot:
                return 'Photo Screenshot';
                break;
            case PHAssetMediaSubtype.PhotoLive:
                return 'Photo Live';
                break;
            case PHAssetMediaSubtype.PhotoDepthEffect:
                return 'Photo Depth Effect';
                break;
            case PHAssetMediaSubtype.VideoStreamed:
                return 'Video Streamed';
                break;
            case PHAssetMediaSubtype.VideoHighFrameRate:
                return 'Video High Frame Rate';
                break;
            case PHAssetMediaSubtype.VideoTimelapse:
                return 'Video Timelapse';
                break;
            default:
                return 'None';
                break;
        }
    }

    /**
     * Get playback style
     * @param style
     * @returns string
     */
    getPlaybackStyle(style) {
        switch (style) {
            case PHAssetPlaybackStyle.Image:
                return 'Image';
                break;
            case PHAssetPlaybackStyle.ImageAnimated:
                return 'Image Animated';
                break;
            case PHAssetPlaybackStyle.LivePhoto:
                return 'Live Photo';
                break;
            case PHAssetPlaybackStyle.Video:
                return 'Video';
                break;
            case PHAssetPlaybackStyle.VideoLooping:
                return 'Video Looping';
                break;
            default:
                return 'Unsupported'
                break;
        }
    }

    /**
     * Get all video
     * @returns array
     */
    fetchVideos() {
        // console.log('Fetch Videos');
        return this.fetchMedia(PHAssetMediaType.Video);
    }

    /**
     * Get all photos
     * @returns all photos
     */
    fetchPhotos() {
        // console.log('Fetch Photos');
        return this.fetchMedia(PHAssetMediaType.Image);
    }
}
PixsaOJ commented 10 months ago

Very old issue, using latest everything. I am making use of NFC but i don't know if its related.