swiftswiftuiconcurrency

Main actor-isolated property 'xxx' can not be referenced from a Sendable closure


I'm having an issue with my photo app after upgrading to Swift 6. In the example below, I’m getting this error (Main actor-isolated property 'photoImage' can not be referenced from a Sendable closure). Does anyone know how to solve this? Assigning it to a different value in .task on launch doesn't work either.

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()) {
           // I’m getting an error here
            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())
}

Solution

  • A possible work-around is to retire the actor-isolated property and create a separate View. And we can use a capture list to grab the thumbnail from the actor-isolated camera, too:

    struct ContentView: View {
        @State var camera = CameraModel()
        @State var selectedItems: [PhotosPickerItem] = []
    
        var body: some View {
            PhotosPicker(selection: $selectedItems, photoLibrary: .shared()) { [thumbnail = camera.thumbnail] in
                PhotoImage(thumbnail: thumbnail)
            }
        }
    }
    
    struct PhotoImage: View {
        let thumbnail: String?
    
        var body: some View {
            if let thumbnail {
                Image(thumbnail)
                    .resizable()
                    .aspectRatio(contentMode: .fill)
                    .animation(.easeInOut(duration: 0.3), value: thumbnail)
            } else {
                Image(systemName: "photo.on.rectangle")
            }
        }
    }