mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 02:29:55 +02:00
refactor(apple): adopt Swift 6 language mode
Enable complete strict concurrency and switch the app target to Swift 6. - Isolate model dependency protocols to @MainActor - Make the CoreRepository blocking-FFI bridge race-free: nonisolated(unsafe) core handle, nonisolated runCore/readSnapshot, @Sendable work block, Sendable domain models - Fix Binding method-reference captures; @preconcurrency imports for CoreNFC/AVFoundation/VnidropCore; isolate the NFC/QR delegate helpers
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
import AVFoundation
|
||||
import CoreNFC
|
||||
@preconcurrency import AVFoundation
|
||||
@preconcurrency import CoreNFC
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
@MainActor
|
||||
@@ -98,6 +98,8 @@ final class IosReceiveInvitationActions: NSObject, ReceiveInvitationActions, UID
|
||||
case .authorized:
|
||||
completion(true)
|
||||
case .notDetermined:
|
||||
// The permission callback is delivered back on the main queue.
|
||||
nonisolated(unsafe) let completion = completion
|
||||
AVCaptureDevice.requestAccess(for: .video) { granted in
|
||||
DispatchQueue.main.async { completion(granted) }
|
||||
}
|
||||
@@ -183,11 +185,14 @@ final class QrScannerViewController: UIViewController, AVCaptureMetadataOutputOb
|
||||
DispatchQueue.global(qos: .userInitiated).async { [session] in session.startRunning() }
|
||||
}
|
||||
|
||||
func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
||||
// The metadata output delegate queue is `.main`, so hop back onto the main
|
||||
// actor to touch view-controller state.
|
||||
nonisolated func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
|
||||
let value = metadataObjects
|
||||
.compactMap { $0 as? AVMetadataMachineReadableCodeObject }
|
||||
.first { $0.type == .qr }?.stringValue?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
if !value.isEmpty { finish(.success(value)) }
|
||||
guard !value.isEmpty else { return }
|
||||
MainActor.assumeIsolated { finish(.success(value)) }
|
||||
}
|
||||
|
||||
private func finish(_ result: Result<String, Error>) {
|
||||
@@ -203,7 +208,8 @@ final class QrScannerViewController: UIViewController, AVCaptureMetadataOutputOb
|
||||
}
|
||||
|
||||
/// NFC invitation reader, ported from `InvitationNfcReader`.
|
||||
final class InvitationNfcReader: NSObject, NFCNDEFReaderSessionDelegate {
|
||||
// Runs entirely on the NFC session's `.main` delegate queue.
|
||||
final class InvitationNfcReader: NSObject, NFCNDEFReaderSessionDelegate, @unchecked Sendable {
|
||||
private let onResult: (Result<String, Error>) -> Void
|
||||
private var session: NFCNDEFReaderSession?
|
||||
private var finished = false
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
import CoreNFC
|
||||
@preconcurrency import CoreNFC
|
||||
|
||||
@MainActor
|
||||
func makePlatformShareActions() -> TransferShareActions { IosTransferShareActions() }
|
||||
@@ -63,7 +63,8 @@ final class IosTransferShareActions: NSObject, TransferShareActions {
|
||||
|
||||
/// Writes a VniDrop invitation to a writable NDEF tag, ported from
|
||||
/// `InvitationNfcWriter` in `TransferShareActions.ios.kt`.
|
||||
final class InvitationNfcWriter: NSObject, NFCNDEFReaderSessionDelegate {
|
||||
// Runs entirely on the NFC session's `.main` delegate queue.
|
||||
final class InvitationNfcWriter: NSObject, NFCNDEFReaderSessionDelegate, @unchecked Sendable {
|
||||
private let ticket: String
|
||||
private let onResult: (Result<Void, Error>) -> Void
|
||||
private var session: NFCNDEFReaderSession?
|
||||
@@ -95,9 +96,13 @@ final class InvitationNfcWriter: NSObject, NFCNDEFReaderSessionDelegate {
|
||||
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {}
|
||||
|
||||
func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag]) {
|
||||
guard let tag = tags.first else {
|
||||
guard let firstTag = tags.first else {
|
||||
return finish(.failure(InvitationError.message("No NFC tag was detected")))
|
||||
}
|
||||
// CoreNFC completion handlers run on the session's `.main` queue; these
|
||||
// framework values are safe to use there.
|
||||
nonisolated(unsafe) let session = session
|
||||
nonisolated(unsafe) let tag = firstTag
|
||||
session.connect(to: tag) { [weak self] connectError in
|
||||
guard let self else { return }
|
||||
if let connectError { return self.finish(.failure(connectError)) }
|
||||
|
||||
Reference in New Issue
Block a user