Как загрузить метаданные из AVAsset в представление SwiftUI без предупреждений компилятора?

Думаю, я делаю вещи по книге. Однако я продолжаю сталкиваться с предупреждениями/ошибками компилятора.

Вот затронутый код, который находится в замыкании View.onChange(of:_:), где currentMediaTitle — это просто String, а player — это просто AVPlayer:

currentMediaTitle = ""

Task {
    guard let asset = player.currentItem?.asset else {
        currentMediaTitle = nil
        return
    }
    
    let allMetadata = try await asset.load(.metadata) // WARNING: Non-sendable type '[AVMetadataItem]' returned by implicitly asynchronous call to nonisolated function cannot cross actor boundary
    
    guard let titleMetadata = allMetadata.first(where: { item in
        item.identifier == AVMetadataIdentifier.commonIdentifierTitle
    })
    else {
        currentMediaTitle = nil
        return
    }
    
    let titleValue = try await titleMetadata.load(.value) // WARNING: Passing argument of non-sendable type 'AVMetadataItem' outside of main actor-isolated context may introduce data races
    
    let title = (titleValue as? NSString) as String?
    
    self.currentMediaTitle = title
}

вот версия скриншота, если вам так удобнее:

Итак, есть несколько вещей, которые... кажутся мне парадоксальными:

  1. Старые способы использования таких вещей, как .metadata , устарели в пользу этого нового метода структурированного параллелизма .load(.metadata), но в то же время значение, возвращаемое этим новым методом, также нарушается. контракты структурированного параллелизма
  2. Кажется, по какой-то причине он рассматривает AVAsset и AVMetadataItem как actor, хотя они оба class
  3. Там написано, что я передаю аргумент типа AVMetadataItem, но вызываю для него метод. Возможно, я могу понять, как он так думает, потому что Swift, похоже, рассматривает методы как статические функции, которые принимают self в качестве первого аргумента, но даже в этом случае я не понимаю, как это может нарушить структурированный параллелизм, поскольку он самоограничен.

Я даже попробовал несколько вещей, чтобы все это произошло на MainActor, но он все равно выдавал мне те же самые предупреждения, так что... Я как бы застрял здесь.

Что мне делать, чтобы устранить эти предупреждения?

Сможешь Task.detached? Где бы ни находился этот код, он находится в изолированном от основного актера контексте, и я не думаю, что его следует запускать на главном актере.

Sweeper 10.06.2024 03:32

У меня такое ощущение, что мы получим ответы на эти вопросы завтра на WWDC, когда будут говорить о Swift 6 и Xcode 16.

dalton_c 10.06.2024 03:43

@Sweeper, я попробую, спасибо. Это в View.onChange(of:_:). Я согласен, что он должен работать в фоновом режиме, но в предупреждении прямо говорится, что это проблема: «Передача аргумента неотправляемого типа AVMetadataItem вне основного контекста, изолированного от актера, может привести к гонкам данных»

Ky - 10.06.2024 04:07

@dalton_c Я на это надеюсь, но боюсь, что это будет несколько месяцев критических ошибок, отзывов, которые попадут в Void Hole, противодействия со стороны сопровождающих Swift, прежде чем они поймут варианты использования, и ворчащих боджей / клуджей.

Ky - 10.06.2024 04:09

@Sweeper Task.detached кажется, это то, что мне нужно. Спасибо! Пожалуйста, напишите это в качестве ответа, чтобы я мог принять его :3

Ky - 10.06.2024 04:51
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
5
146
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Код в Task { ... } изолирован от главного актера, а load — нет, поэтому неотправляемые типы, такие как AVMetadataItem, не могут передаваться между ними, пересекая границы актеров.

Вы можете использовать Task.detached для запуска кода главного актера, но при этом все равно будет захватываться self (при условии, что currentMediaTitle является членом self), который, вероятно, не подлежит отправке. Вы по-прежнему получите предупреждение, если включите строгую проверку параллелизма.

Кажется, вы извлекаете заголовок какого-то AVAsset, поэтому я бы написал это как расширение AVAsset:

extension AVAsset {
    func mediaTitle() async throws -> String? {
        let allMetadata = try await load(.metadata)
        guard let titleMetadata = allMetadata.first(where: { item in
            item.identifier == AVMetadataIdentifier.commonIdentifierTitle
        })
        else {
            return nil
        }
        let titleValue = try await titleMetadata.load(.value)
        let title = (titleValue as? NSString) as String?
        return title
    }
}

и теперь вы можете сделать:

Task {
    currentMediaTitle = try await player.currentItem?.asset.mediaTitle()
}

Это снова отправляет AVAsset через границы актеров, и вы получите предупреждение, если включите строгую проверку параллелизма. Вы можете отключить это предупреждение, используя @preconcurrency import AVFoundation, и это, вероятно, не будет проблемой после того, как будет реализована изоляция субъектов на основе региона.

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