0

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

Lance Samaria
  • 17,576
  • 18
  • 108
  • 256
  • Your variable name and function name are same `videoOrientation ` maybe that can be issue...and declare that variable outside the function to make it accessible – Nayan Dave Dec 14 '19 at 12:48
  • Hi I tried to access it outside the function it still said it needs to be called from the main thread – Lance Samaria Dec 14 '19 at 13:49

1 Answers1

0

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

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • Hi, I actually tried that but another issue occurred. It said “Cannot return expression of type () to return type AVCaptureVideoOrientation”. wrapped everything inside the entire function inside DispatchQueue.main.async – Lance Samaria Dec 14 '19 at 13:21
  • I added the code that uses it. it's inside the startRecording() function – Lance Samaria Dec 14 '19 at 13:47
  • where you call `startRecording` ? – Shehata Gamal Dec 14 '19 at 13:49
  • It's a button, that calls it. Once the cameraButton is pressed it calls that function. I do some other checks in there to make sure the user's permission is granted and if so then the startRecording() function gets called. – Lance Samaria Dec 14 '19 at 13:51
  • 1
    that's the reason the closure of the permission check runs in a background thread wrap that function inside `DispatchQueue.main.async {` – Shehata Gamal Dec 14 '19 at 13:54