I am working on an app that uses an AVCaptureLayer to display a video preview layer and also a text representation of the brightness (calculated from the video metadata). I've tried to make my app more efficient by only creating one AVCaptureSession in a class VideoStream and I want to then pass the capture session to a UIViewRepresentable that creates a UIView showing the video preview layer. The View is straightforward:
struct ContentView: View {
@StateObject var videoStream = VideoStream() // holds session variable for AVCaptureSession
var body: some View {
VStack {
VideoPreviewHolder(runningSession: videoStream.session) // videoStream.session not initialized at build time
}.frame(width: 300, height: 300, alignment: .center)
Text(String(format: "%.2f Lux", videoStream.luminosityReading))
.font(.largeTitle)
}
}
Does SwiftUI provide a way to await the initialization of a StateObject class? The init() method for VideoStream() calls a function responsible for first checking for camera authorization and if necessary requesting it, and if authorization is granted then a second function is called which configures the AVCaptureSession. This all takes quite a bit of time, especially if the user needs to grant or deny the authorization.
I've tried adding await / async to the VideoStream() initializer and the functions called therein, but since my VideoStream() class inherits from NSObject, I receive an error that async initializer cannot be represented in Objective-C.
Is there a SwiftUI specific way of awaiting the StateObject's initialization?
CodePudding user response:
Make VideoStream's session property optional and published:
class VideoStream: ObservableObject {
@Published var session: AVCaptureSession?
// blah blah blah
}
Don't set the property until the session is fully configured. Make sure you dispatch back to the main queue/thread/actor to set it if needed.
Then make your view check whether the property is nil:
struct ContentView: View {
@StateObject var videoStream = VideoStream()
var body: some View {
if let session = videoStream.session {
VStack {
VideoPreviewHolder(runningSession: session)
}.frame(width: 300, height: 300, alignment: .center)
Text(String(format: "%.2f Lux", videoStream.luminosityReading))
.font(.largeTitle)
} else {
ProgressView()
}
}
}
