Как преобразовать VNDocumentCameraViewControllerDelegate в параллелизм Swift 6

Итак, у меня есть этот код:

fileprivate class DocumentScanDelegate: NSObject, VNDocumentCameraViewControllerDelegate {
  
  static let shared = DocumentScanDelegate()

  var compressionQuality: CGFloat = 1
  var onScanSuccess: (UIImage) -> Void = { _ in }
  
  func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
    
    controller.dismiss(animated: true)
    
    guard scan.pageCount >= 1 else { return }
    let lastPage = scan.imageOfPage(at: scan.pageCount - 1)
    let compressed = lastPage.compressed(quality: compressionQuality)
    onScanSuccess(compressed)
  }
  
  func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
    controller.dismiss(animated: true)
  }
  
  func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
    controller.dismiss(animated: true)
  }
  

}

У меня 2 ошибки:

  1. shared:

Статическое свойство «shared» не является безопасным для параллелизма, поскольку тип «DocumentScanDelegate», отличный от «Sendable», может иметь общее изменяемое состояние; это ошибка в языковом режиме Swift 6

  1. 3 controller.dismiss звонят:

Вызов основного метода экземпляра, изолированного от актера, 'dismiss(animated:completion:)' в синхронном неизолированном контексте; это ошибка в языковом режиме Swift 6

И то, и другое имеет смысл. Итак, я добавил @MainActor к DocumentScanDelegate . Затем первое предупреждение исчезло. Но второе предупреждение становится:

Главный метод экземпляра, изолированный от актера, 'documentCameraViewController(_:didFinishWith:)' не может использоваться для удовлетворения требований неизолированного протокола; это ошибка в языковом режиме Swift 6

Затем я использую @preconcurrency для аннотации VNDocumentCameraViewControllerDelegate соответствия:

@MainActor
fileprivate class DocumentScanDelegate: NSObject, @preconcurrency VNDocumentCameraViewControllerDelegate {
  ...
}

Этот @preconcurrency трюк очень похож на видео WWDC 2024 (https://developer.apple.com/videos/play/wwdc2024/10169/?time=1520)

Ошибка становится:

Главный метод экземпляра, изолированный от актера, 'documentCameraViewController(_:didFinishWith:)' не может использоваться для удовлетворения требований неизолированного протокола; это ошибка в языковом режиме Swift 6

Со следующей дополнительной ошибкой:

Класс «VNDocumentCameraScan» не соответствует протоколу «Sendable».

Затем я использую @preconcurrency импорт:

@preconcurrency import VisionKit

То же предупреждение сохраняется.

Также есть новое предупреждение (почему?):

Атрибут @preconcurrency в модуле VisionKit не имеет никакого эффекта.

Зачем нужен shared? Почему бы просто не создавать DocumentScanDelegate только тогда, когда вам это нужно?

Sweeper 09.07.2024 05:44

Потому что я хочу запустить сканирование документов из статической вспомогательной функции, и мне нужен способ сохранить этот делегат

OMGPOP 09.07.2024 05:52

Предупреждение «@preconcurrency не имеет эффекта» напоминает мне об этой проблеме. Импортируете ли вы что-нибудь еще, что также может импортировать VisionKit? Если VisionKit «покрыт» другим импортом без предварительного параллелизма, @preconcurrency import VisionKit не будет иметь никакого эффекта.

Sweeper 09.07.2024 06:11

У меня есть @preconcurrency import Vision в другом файле этого модуля. Хотя я считаю, что VisionKit зависит от Vision, а не наоборот.

OMGPOP 09.07.2024 06:14

Я только что попробовал удалить @preconcurrency вместо Vision и добавить @preconcurrency вместо VisionKit. Затем я получил 2 предупреждения: (1) для Vision: Add '@preconcurrency' to suppress 'Sendable'-related warnings from module 'Vision' и (2) для VisionKit: '@preconcurrency' attribute on module 'VisionKit' has no effect.

OMGPOP 09.07.2024 06:16

Удалите весь импорт VisionKit из файла. Файл все еще компилируется? Если это так, это означает, что импортируется что-то еще VisionKit, и выясните, что это такое.

Sweeper 09.07.2024 06:21

Нет, не компилируется. Я также просмотрел всю кодовую базу и обнаружил, что это единственные 2 файла, которые импортируют фреймворки, связанные со зрением (один импортирует Vision, а другой VisionKit). На всякий случай я также удалил файл, который импортирует Vision, и все равно предупреждение то же самое.

OMGPOP 09.07.2024 06:27

> «Затем я использую @preconcurrency для аннотации соответствия VNDocumentCameraViewControllerDelegate:» Почему бы просто не обернуть вызовы controller в задачу вместо добавления @MainActor ко всему классу? Task { @MainActor in controller.dismiss(animated: true) } Функции протокола VNDocumentCameraViewControllerDelegate не гарантированно будут вызваны @MainActor, если аннотация отсутствует в определении протокола (также я не могу найти ничего о том, что она вызывается в основном потоке документации).

fabianm 12.07.2024 12:57

@fabianm, вы не можете передать UIViewController через границу изоляции, поскольку он не подлежит отправке (если я что-то не упускаю? Не могли бы вы написать ответ? Спасибо)

OMGPOP 13.07.2024 05:23
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
76
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Продолжение нашего разговора в комментариях здесь.

Учитывая ваш последний вопрос, я не совсем уверен, почему это работает, если честно, но я попробовал это со строгой проверкой параллелизма, установленной на «завершение», и он не жалуется. Я предполагаю, что захватить controller в закрытии задачи не проблема, потому что доступ к нему гарантированно будет возможен только там, где @MainActor.

import VisionKit

private class DocumentScanDelegate: NSObject, VNDocumentCameraViewControllerDelegate {
    @MainActor
    static let shared = DocumentScanDelegate()

    var compressionQuality: CGFloat = 1
    var onScanSuccess: (UIImage) -> Void = { _ in }

    func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
        Task { @MainActor in 
            controller.dismiss(animated: true)
        }
        
        guard scan.pageCount >= 1 else { return }
        let lastPage = scan.imageOfPage(at: scan.pageCount - 1)
        let compressed = lastPage.compressed(quality: compressionQuality)
        onScanSuccess(compressed)
    }

    func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
        Task { @MainActor in
            controller.dismiss(animated: true)
        }
    }

    func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
        Task { @MainActor in
            controller.dismiss(animated: true)
        }
    }
}

вы включили полную проверку в настройках сборки?

OMGPOP 16.07.2024 07:53

Да, установлено значение complete. Я также пытался использовать Swift 6, и он тоже не жалуется.

fabianm 16.07.2024 17:57

Странно, я не вижу здесь предупреждения. Я вижу аннотацию @MainActor в определении Apple VNDocumentCameraViewController - означает ли это, что она также может быть отправлена?

OMGPOP 17.07.2024 05:39

Ах, «Классы, отмеченные @MainActor, неявно доступны для отправки, потому что главный актер координирует весь доступ к своему состоянию. Эти классы могут иметь хранимые свойства, которые являются изменяемыми и неотправляемыми». Из документа Apple Developer.apple.com/documentation/swift/sendable#

OMGPOP 17.07.2024 05:40

Я принимаю этот ответ, но очень подозреваю, что в будущем сам протокол VNDocumentCameraViewControllerDelegate будет помечен как MainActor (поскольку это операции, связанные с пользовательским интерфейсом), к тому времени эта Task операция нам уже не понадобится. Но мы посмотрим.

OMGPOP 17.07.2024 05:43

Спасибо. Вероятно, в этом вы правы. Если вы абсолютно уверены, что он всегда будет вызываться в основном потоке, вы также можете использовать MainActor.assumeIsolated, чтобы избежать планирования асинхронного закрытия контроллера представления.

fabianm 18.07.2024 07:09

Я уверен на 99%. Однако это нигде не задокументировано, поэтому я бы сейчас перестраховался и использовал Task { @MainActor in }

OMGPOP 21.07.2024 07:40

Другие вопросы по теме