Search code examples
iosswiftstatusbardispatch-queue

Swift -access UIApplication.shared.statusBarOrientation from main thread


I'm trying to access the UIApplication.shared.statusBarOrientation but I keep getting the error UIApplication.statusBarOrientation() must be used from main thread only:

enter image description here

I tried to add it and the switch statement that uses it to the mainQueue but that leads to the return value from the function it's contained in to crashing with a nil value:

DispatchQueue.main.async {

    let interfaceOrientation = UIApplication.shared.statusBarOrientation

    switch interfaceOrientation {

    case .portrait, .portraitUpsideDown, .unknown:
        videoOrientation = .portrait
    case .landscapeLeft:
        videoOrientation = .landscapeRight
    case .landscapeRight:
        videoOrientation = .landscapeLeft
    @unknown default:
        break
    }
}

Code:

func videoOrientation() -> AVCaptureVideoOrientation {

    var videoOrientation: AVCaptureVideoOrientation!

    let orientation: UIDeviceOrientation = UIDevice.current.orientation

    switch orientation {

    case .faceUp, .faceDown, .unknown:

        // ** I tried to add this alone to the mainQueue but the switch statement couldn't access it so then I tried to add the entire switch but that lead to the return value crashing with a nil
        let interfaceOrientation = UIApplication.shared.statusBarOrientation

        switch interfaceOrientation {

        case .portrait, .portraitUpsideDown, .unknown:
            videoOrientation = .portrait
        case .landscapeLeft:
            videoOrientation = .landscapeRight
        case .landscapeRight:
            videoOrientation = .landscapeLeft
        @unknown default:
            break
        }

    case .portrait, .portraitUpsideDown:
        videoOrientation = .portrait
    case .landscapeLeft:
        videoOrientation = .landscapeRight
    case .landscapeRight:
        videoOrientation = .landscapeLeft
    @unknown default:
        break
    }

    return videoOrientation // ** crashes here **
}

I access the return value when pressing the camerButton to record video using AVFoundation. The movieFileOutput object accesses the videoOrientation on this line movieFileOutput.connection(with: AVMediaType.video)?.videoOrientation = videoOrientation():

Code:

let captureSession = AVCaptureSession()
var movieFileOutput = AVCaptureMovieFileOutput()
var videoFileUrlFromCamera: URL?

func startRecording() {

    if !captureSession.outputs.contains(movieFileOutput) {
        return
    }

    // Stop recording
    if movieFileOutput.isRecording {

        stopMovieRecordigShowControls()

    } else {

        if !captureSession.outputs.contains(movieFileOutput) {
            return
        }

        // Start recording
        movieFileOutput.connection(with: AVMediaType.video)?.videoOrientation = videoOrientation()

        movieFileOutput.maxRecordedDuration = maxRecordDuration()

        videoFileUrlFromCamera = URL(fileURLWithPath: videoFileLocation())

        if let videoFileUrlFromCamera = videoFileUrlFromCamera {

            movieFileOutput.startRecording(to: videoFileUrlFromCamera, recordingDelegate: self)
        }

    }
}

How can I fix this?

UPDATE I also tried to wrap the entire code inside the DispatchQueue but I got a compile error of Cannot return expression of type () to return type AVCaptureVideoOrientation:

func videoOrientation() -> AVCaptureVideoOrientation {

    DispatchQueue.main.async {

        var videoOrientation: AVCaptureVideoOrientation!

        let orientation: UIDeviceOrientation = UIDevice.current.orientation

        switch orientation {

        case .faceUp, .faceDown, .unknown:

            let interfaceOrientation = UIApplication.shared.statusBarOrientation

            switch interfaceOrientation {

            case .portrait, .portraitUpsideDown, .unknown:
                videoOrientation = .portrait
            case .landscapeLeft:
                videoOrientation = .landscapeRight
            case .landscapeRight:
                videoOrientation = .landscapeLeft
            @unknown default:
                break
            }
            break
        case .portrait, .portraitUpsideDown:
            videoOrientation = .portrait
        case .landscapeLeft:
            videoOrientation = .landscapeRight
        case .landscapeRight:
            videoOrientation = .landscapeLeft
        @unknown default:
            break
        }

        return videoOrientation
    }
}

enter image description here


Solution

  • Dispatching the other code in main thread makes the value var videoOrientation: AVCaptureVideoOrientation! to be nil when you return it as the return fires before the switch , it's better to make sure that you call the whole startRecording() from main thread like

    DispatchQueue.main.async {
        self.startRecording()
    }
    

    As the closure of the permission check runs in a background thread so you need to dispatch the call of the recording inside DispatchQueue.main.async