Swift 6 strict concurrency: How to achieve Sendable conformance for a class bridging C callbacks?

1 week ago 4
ARTICLE AD BOX

I'm working on a macOS app that uses Apple's private MultitouchSupport.framework to intercept raw trackpad touch data. The framework uses C-style callbacks that fire on an internal background thread (mt_ThreadedMTEntry). I'm trying to achieve Swift 6 strict concurrency compliance but running into challenges with the callback pattern.

// Private API bindings using @_silgen_name typealias MTDeviceRef = UnsafeMutableRawPointer typealias MTContactCallbackFunction = @convention(c) ( MTDeviceRef?, UnsafeMutableRawPointer?, // Pointer to touch data array Int32, // Number of touches Double, // Timestamp Int32 // Frame number ) -> Int32 @_silgen_name("MTDeviceStart") func MTDeviceStart(_ device: MTDeviceRef, _ mode: Int32) @_silgen_name("MTRegisterContactFrameCallback") func MTRegisterContactFrameCallback(_ device: MTDeviceRef, _ callback: MTContactCallbackFunction)

Registering the callback:

// C-convention callback — fires on framework's internal thread (mt_ThreadedMTEntry) let callback: MTContactCallbackFunction = { device, touches, count, timestamp, frame in guard let touches = touches else { return 0 } MultitouchManager.shared.deviceDidReceiveTouches(touches, count: count) return 0 } // During setup: if let device = MTDeviceCreateDefault() { MTRegisterContactFrameCallback(device, callback) MTDeviceStart(device, 0) }

My current approach uses @unchecked Sendable conformance with internal locking:

final class MultitouchManager: @unchecked Sendable { private let fingerCountLock = NSLock() private var _currentFingerCount: Int = 0 var currentFingerCount: Int { get { fingerCountLock.lock() defer { fingerCountLock.unlock() } return _currentFingerCount } set { fingerCountLock.lock() defer { fingerCountLock.unlock() } _currentFingerCount = newValue } } private let gestureQueue = DispatchQueue(label: "com.app.gesture", qos: .userInteractive) }

My Question:

Is @unchecked Sendable the right approach here? The framework's callback gives us no control over which thread it uses, and we can't mark the C function type as @Sendable.

Environment: macOS 15+, Swift 6.0, Xcode 16+

Note: I'm aware this uses private APIs. This is for a utility app distributed outside the App Store.

Read Entire Article