I am testing some swift 6 migrations in xcode 16.1 beta and I am trying to migrate the Apple CamAV app that was created in wwdc24 to swift 6 but I don't know how to fix this error in the init.
Errors:
Call to main actor-isolated initializer 'init(frame:)' in a synchronous nonisolated context; this is an error in the Swift 6 language mode
For all imageView properties.
Main actor-isolated property 'contentMode' can not be mutated from a nonisolated context; this is an error in the Swift 6 language mode
class PreviewView: UIView, PreviewTarget {
init() {
super.init(frame: .zero)
#if targetEnvironment(simulator)
// The capture APIs require running on a real device. If running
// in Simulator, display a static image to represent the video feed.
let imageView = UIImageView(frame: UIScreen.main.bounds)
imageView.image = UIImage(named: "video_mode")
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(imageView)
#endif
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Use the preview layer as the view's backing layer.
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
var previewLayer: AVCaptureVideoPreviewLayer {
layer as! AVCaptureVideoPreviewLayer
}
nonisolated func setSession(_ session: AVCaptureSession) {
// Connects the session with the preview layer, which allows the layer
// to provide a live view of the captured content.
Task { @MainActor in
previewLayer.session = session
}
}
}
Came here while doing the same thing, this compilation error feels very weird even tho the reason is kinda clear (NSObject empty init is not mainActor-isolated) and reproducible for any CocoaView subclass.
Strict checks in swift6 with no ability to locally disable them gonna make me insane
I feel this meme on every level of my soul
Tho this thing seems to compile
@MainActor
class PreviewView: UIView, CameraPreviewTarget {
var onCaptureDevicePointTap: ((CGPoint) -> Void)?
override class var layerClass: AnyClass {
AVCaptureVideoPreviewLayer.self
}
var previewLayer: AVCaptureVideoPreviewLayer {
layer as! AVCaptureVideoPreviewLayer
}
convenience init() {
self.init()
}
@MainActor
@_disfavoredOverload
init(_ void: Void = ()) {
super.init(frame: .zero)
#if targetEnvironment(simulator)
// The capture APIs require running on a real device. If running
// in Simulator, display a static image to represent the video feed.
let imageView = UIImageView(frame: UIScreen.main.bounds)
imageView.image = UIImage.resource(.photoMode)
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(imageView)
#endif
addGestureRecognizer(
UITapGestureRecognizer(
target: self,
action: #selector(handleTap)
))
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
nonisolated func setSession(_ session: AVCaptureSession) {
let sendable = UncheckedSendable(session)
Task { @MainActor in
previewLayer.session = sendable.wrappedValue
}
}
@objc private func handleTap(from recognizer: UITapGestureRecognizer) {
let captureDevicePoint = previewLayer.captureDevicePointConverted(
fromLayerPoint: recognizer.location(in: self)
)
onCaptureDevicePointTap?(captureDevicePoint)
}
}
Upd:
Yes it's compiling, because it recoursively calls itself
Simply remove empty init and provide default value for init(frame:)
override init(frame: CGRect = .zero) {
super.init(frame: frame)
#if targetEnvironment(simulator)
// The capture APIs require running on a real device. If running
// in Simulator, display a static image to represent the video feed.
let imageView = UIImageView(frame: UIScreen.main.bounds)
imageView.image = UIImage.resource(.photoMode)
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addSubview(imageView)
#endif
addGestureRecognizer(UITapGestureRecognizer(
target: self,
action: #selector(handleTap)
))
}