AVCam sample code build errors in Swift 6

The AVCam sample code by Apple fails to build in Swift 6 language settings due to failed concurrency checks ((the only modification to make in that code is to append @preconcurrency to import AVFoundation).

Here is a minimally reproducible sample code for one of the errors:

import Foundation

final class Recorder {
    
    var writer = Writer()
    var isRecording = false
    
    func startRecording() {
        Task { [writer] in
            await writer.startRecording()
            print("started recording")
        }
    }
    
    func stopRecording() {
        Task { [writer] in
            await writer.stopRecording()
            print("stopped recording")
        }
    }
    
    func observeValues() {
        
        Task {
            for await value in await writer.$isRecording.values {
                isRecording = value
            }
        }
    }
}

actor Writer {
    @Published private(set) public var isRecording = false
    
    func startRecording() {
        isRecording = true
    }
    
    func stopRecording() {
        isRecording = false
    }
}

The function observeValues gives an error:

Non-sendable type 'Published<Bool>.Publisher' in implicitly asynchronous access to actor-isolated property '$isRecording' cannot cross actor boundary

I tried everything to fix it but all in vain. Can someone please point out if the architecture of AVCam sample code is flawed or there is an easy fix?

Answered by DTS Engineer in 820261022

Hello @testinstadev,

Please file an enhancement request using Feedback Assistant to request that the AVCam sample be updated for Swift 6.

For your minimal reproducing example, I recommend that you continue to follow-up in your thread on the Swift Forums (https://forums.swift.org/t/swift-6-concurrency-error-of-passing-sending-closure/77048).

-- Greg

Hello @testinstadev,

Please file an enhancement request using Feedback Assistant to request that the AVCam sample be updated for Swift 6.

For your minimal reproducing example, I recommend that you continue to follow-up in your thread on the Swift Forums (https://forums.swift.org/t/swift-6-concurrency-error-of-passing-sending-closure/77048).

-- Greg

@DTS Engineer I spent sometime and enabled concurrency checks to complete in the compiler options. I was able to fix two main sources of warning (which are error in Swift 6) by changing the definition of two functions as follows:


   func capturePhoto(with features: PhotoFeatures,  isolation: isolated (any Actor)? = #isolation) async throws -> Photo

  func stopRecording(_ isolation: isolated (any Actor)? = #isolation) async throws -> Movie 

Effectively, I forced these functions to be called from the isolation domain of an Actor (which is actor CaptureService in the source code). I believe this should be okay as this is the only place where these functions are invoked.

However, there is one place in the code where I still need help:

override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
       switch keyPath {
       case systemPreferredKeyPath:
           // Update the observer's system-preferred camera value.
           let newDevice = change?[.newKey] as? AVCaptureDevice
           continuation?.yield(newDevice)
       default:
           super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
       }
   }

The line continuation?.yield(newDevice) throws a warning that Task-isolated 'newDevice' is passed as a 'sending' parameter; Uses in callee may race with later task-isolated uses.

Is this because AVCaptureDevice is not Sendable?

The line continuation?.yield(newDevice) throws a warning that Task-isolated 'newDevice' is passed as a 'sending' parameter; Uses in callee may race with later task-isolated uses.

Is this because AVCaptureDevice is not Sendable?

Correct, you are receiving that error because AVCaptureDevice is not Sendable, and the object parameter of observeValue is not sending.

So, what can you do?

You can utilize @unchecked Sendable to resolve the compiler errors. When you do this, "you are responsible for the correctness of unchecked sendable types, for example, by protecting all access to its state with a lock or a queue."

In many cases, AVCaptureDevice already requires you to call lockForConfiguration when you modify its state anyway.

-- Greg

AVCam sample code build errors in Swift 6
 
 
Q