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()
}
}
}