Вопрос или проблема
У меня возникла проблема с моим фото-приложением после обновления до Swift 6. В приведенном ниже примере я получаю эту ошибку (Свойство ‘photoImage’, изолированное для главного потока, не может быть вызвано из замыкания Sendable). Кто-нибудь знает, как это исправить? Присваивание ему другого значения в .task при запуске тоже не работает.
import SwiftUI
import PhotosUI
@Observable
final class CameraModel {
var thumbnail: String?
}
struct ContentView: View {
@State var camera = CameraModel()
@State var selectedItems: [PhotosPickerItem] = []
var body: some View {
PhotosPicker(selection: $selectedItems, photoLibrary: .shared()) {
// Я получаю ошибку здесь
photoImage
}
}
@ViewBuilder
private var photoImage: some View {
if let thumbnail = camera.thumbnail {
Image(thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
.animation(.easeInOut(duration: 0.3), value: thumbnail)
} else {
Image(systemName: "photo.on.rectangle")
}
}
}
#Preview {
ContentView(camera: CameraModel())
}
Просто пометьте ваш photoImage
как nonisolated
, потому что протокол View
помечен как @MainActor
, поэтому все его свойства привязаны к MainActor
. Тип метки замыкания просто @Sendable label: () -> Label
, это не изолированный контекст, поэтому вам нужно пометить nonisolated
.
struct ContentView: View {
//@State var camera = CameraModel()
@State var selectedItems: [PhotosPickerItem] = []
var body: some View {
PhotosPicker(selection: $selectedItems, photoLibrary: .shared(), label: {
photoImage
})
}
@ViewBuilder
nonisolated private var photoImage: some View { //<- отметьте здесь
...
}
}
.
Ответ или решение
Ошибка "Main actor-isolated property ‘xxx’ cannot be referenced from a Sendable closure" возникает из-за обновлений в Swift 6, касающихся управления контекстом исполнения и параметрами потокобезопасности. Давайте разберем вашу проблему, чтобы понять, как ее решить.
Контекст ошибки
Как правило, данный тип ошибки свидетельствует о том, что код пытается получить доступ к свойству (или переменной), изолированному в контексте главного актора, из замыкания, помеченного как Sendable
. Swift 6 усилил безопасность потоков, заставляя разработчиков явно указывать, что можно безопасно передавать между потоками.
В вашем коде вы используете свойство photoImage
, которое является представлением, зависящим от состояния модели camera
, в замыкании, переданном в PhotosPicker
. Однако, поскольку PhotoPicker
работает с контекстом, который может исполняться в другом потоке, Swift не разрешает доступ к свойствам, изолированным для главного актора.
Решение проблемы
Чтобы устранить данный конфликт, вы можете пометить ваше представление photoImage
как nonisolated
. Это позволит избежать ограничения доступа из замыкания, вызванного контекстом исполнения:
struct ContentView: View {
@State var selectedItems: [PhotosPickerItem] = []
var body: some View {
PhotosPicker(selection: $selectedItems, photoLibrary: .shared()) {
photoImage
}
}
@ViewBuilder
nonisolated private var photoImage: some View {
if let thumbnail = camera.thumbnail {
Image(thumbnail)
.resizable()
.aspectRatio(contentMode: .fill)
.animation(.easeInOut(duration: 0.3), value: thumbnail)
} else {
Image(systemName: "photo.on.rectangle")
}
}
}
Подробное объяснение изменений
-
Объявление
nonisolated
: Добавление этого ключевого слова указывает, что данный метод или свойство может быть безопасно вызвано из любого контекста, не нарушая правил безопасности потоков. Это позволяет вам избежать ошибок, связанных с изоляцией актора. -
Переменная
@State
: Обратите внимание также на то, что переменные состояния (@State
) должны использоваться с осторожностью в контексте изоляции. Убедитесь, что данные, хранящиеся в ваших состояниях, не конфликтуют с потокобезопасностью.
Заключение
Использование конструкции nonisolated
является одним из способов эффективно управлять доступом к изолированным свойствам в Swift 6. Если вы сталкиваетесь с аналогичными ошибками в будущем, всегда стоит проверять контекст, в котором выполняется ваш код, и использовать соответствующие модификаторы, чтобы соответствовать требованиям безопасности потоков.
Следуя этим рекомендациям, вы сможете устранить возникшую проблему и оптимизировать ваш код для работы в среде Swift 6.