Search code examples
swiftios13

*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'threading violation: expected the main thread'


I'm having an issue where our application is crashing when the user taps the profile picture to upload their photo to the app. The app sends an authorization request to the user, and this crash happens immediantly once the "allow" button is tapped.

Maybe relevant information is that this is not the root view controller. The registration process has a series of pages on the same Storyboard. This is occuring on any page where I need to request permissions that isn't the main root controller.

I have attempted to place the PhotoAuthorization block of code into a DispatchQueue.main.async, but that didn't seem to work. This is legacy code by a former developer, so I'm still working on fixing up some stuff.

Code block suspected of crashing the app:

    func checkPermission() {
        let photoAuthStatus = PHPhotoLibrary.authorizationStatus()
        switch photoAuthStatus {
        case .authorized:
            self.showPhotoActionSheet()
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization({ (newStatus) in
                if newStatus == PHAuthorizationStatus.authorized {
                    self.showPhotoActionSheet()
                }
            })
        case .restricted:
            showPermissionRequestReason()
        case .denied:
            showPermissionRequestReason()
        }
    }

    func showPhotoActionSheet() {
        let actionSheet = YoutubeActionController()
        actionSheet.addAction(Action(ActionData(title: "Take Photo", image: UIImage(named: "ic_photo_camera")!), style: .default, handler: { action in 
            if UIImagePickerController.isSourceTypeAvailable(.camera) {
                self.imagePicker.sourceType = .camera
                self.imagePicker.allowsEditing = true
                self.present(self.imagePicker, animated: true, completion: nil)
            }
        }))
        actionSheet.addAction(Action(ActionData(title: "Choose from Camera Roll", image: UIImage(named: "ic_photo_album")!), style: .default, handler: { action in 
            if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum) {
                self.imagePicker.sourceType = .savedPhotosAlbum
                self.imagePicker.allowsEditing = true
                self.present(self.imagePicker, animated: true, completion: nil)
            }
        }))
        actionSheet.addAction(Action(ActionData(title: "Cancel", image: UIImage(named: "ic_cancel")!), style: .cancel, handler: nil))
        present(actionSheet, animated: true, completion: nil)
    }

Find the traceback below. Any help is greatly appreciated!

2020-01-23 13:39:23.878772+0000 ***[89799:17598462] *** Assertion failure in -[FBSSerialQueue assertOnQueue], /BuildRoot/Library/Caches/com.apple.xbs/Sources/FrontBoardServices_Sim/FrontBoard-626.2/FrontBoardServices/FBSSerialQueue.m:98
2020-01-23 13:39:23.990535+0000 ***[89799:17598462] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'threading violation: expected the main thread'
*** First throw call stack:
(
    0   CoreFoundation                      0x0000000115f7102e __exceptionPreprocess + 350
    1   libobjc.A.dylib                     0x0000000115ddeb20 objc_exception_throw + 48
    2   CoreFoundation                      0x0000000115f70da8 +[NSException raise:format:arguments:] + 88
    3   Foundation                          0x000000010f613b61 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 191
    4   FrontBoardServices                  0x000000011c344a8b -[FBSSerialQueue assertOnQueue] + 236
    5   FrontBoardServices                  0x000000011c2f77b9 -[FBSSceneImpl updateClientSettings:withTransitionContext:] + 70
    6   FrontBoardServices                  0x000000011c2f7a04 -[FBSSceneImpl updateClientSettingsWithTransitionBlock:] + 154
    7   FrontBoardServices                  0x000000011c2f7929 -[FBSSceneImpl updateClientSettingsWithBlock:] + 110
    8   UIKitCore                           0x00000001200c7aa0 -[FBSScene(UIApp) updateUIClientSettingsWithBlock:] + 160
    9   UIKitCore                           0x000000011fcbf5f6 -[_UISystemAppearanceManager updateScreenEdgesDeferringSystemGestures] + 374
    10  UIKitCore                           0x000000011ff229e2 __70-[UIViewController setNeedsUpdateOfScreenEdgesDeferringSystemGestures]_block_invoke_2 + 118
    11  UIKitCore                           0x000000011ff00cb5 -[UIViewController _updateSystemAppearanceWithRecursionBlock:action:] + 295
    12  UIKitCore                           0x000000011ff22629 -[UIViewController _setPresentedStatusBarViewController:] + 220
    13  UIKitCore                           0x000000011ff129c3 -[UIViewController _presentViewController:modalSourceViewController:presentationController:animationController:interactionController:completion:] + 1381
    14  UIKitCore                           0x000000011ff143c6 -[UIViewController _presentViewController:withAnimationController:completion:] + 4349
    15  UIKitCore                           0x000000011ff16c47 __63-[UIViewController _presentViewController:animated:completion:]_block_invoke + 98
    16  UIKitCore                           0x000000011ff1715f -[UIViewController _performCoordinatedPresentOrDismiss:animated:] + 511
    17  UIKitCore                           0x000000011ff16ba5 -[UIViewController _presentViewController:animated:completion:] + 187
    18  UIKitCore                           0x000000011ff16e0c -[UIViewController presentViewController:animated:completion:] + 150
    19  ***                      0x000000010dc472ca $s14***26RegistrationViewControllerC20showPhotoActionSheetyyF + 2522
    20  ***                      0x000000010dc462b9 $s14***26RegistrationViewControllerC15checkPermissionyyFySo21PHAuthorizationStatusVcfU_ + 201
    21  ***                      0x000000010dc46355 $sSo21PHAuthorizationStatusVIegy_ABIeyBy_TR + 53
    22  Photos                              0x0000000116a72643 __39+[PHPhotoLibrary requestAuthorization:]_block_invoke + 52
    23  AssetsLibraryServices               0x000000013927ff7e __79-[PLPrivacy _isPhotosAccessAllowedWithScope:forceHandler:accessAllowedHandler:]_block_invoke.14 + 501
    24  AssetsLibraryServices               0x000000013924b60c __pl_dispatch_async_block_invoke + 25
    25  libdispatch.dylib                   0x0000000117a02848 _dispatch_call_block_and_release + 12
    26  libdispatch.dylib                   0x0000000117a037b9 _dispatch_client_callout + 8
    27  libdispatch.dylib                   0x0000000117a09526 _dispatch_lane_serial_drain + 707
    28  libdispatch.dylib                   0x0000000117a09f5c _dispatch_lane_invoke + 388
    29  libdispatch.dylib                   0x0000000117a13ff9 _dispatch_workloop_worker_thread + 626
    30  libsystem_pthread.dylib             0x00007fff51bfd611 _pthread_wqthread + 421
    31  libsystem_pthread.dylib             0x00007fff51bfd3fd start_wqthread + 13
)
libc++abi.dylib: terminating with uncaught exception of type NSException

Solution

  • The error tells you want to do:

    reason: 'threading violation: expected the main thread'

    Many times, when you pass a closure, you could be called back on a background thread. But, if you want to do anything with the UI, that must be done on the main thread. We use DispatchQueue.main.async to pass a block to the main queue and have it run on the main thread asynchronously,

    In

    PHPhotoLibrary.requestAuthorization({ (newStatus) in
        if newStatus == PHAuthorizationStatus.authorized {
            self.showPhotoActionSheet()
        }
    })    
    

    You need to dispatch to the main thread

    PHPhotoLibrary.requestAuthorization({ (newStatus) in
        if newStatus == PHAuthorizationStatus.authorized {
            DispatchQueue.main.async {
                self.showPhotoActionSheet()
            }
    
        }
    })    
    

    It looks like you might be calling checkPermission on the background. If so, you also need to wrap the call to:

    self.showPhotoActionSheet()
    

    From the call stack -- it looks like this might be the one you are having problems with.