SwiftUI Main Actor Isolation Error with PhotosPicker

I'm getting the following error in my SwiftUI code:

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

I don't understand how to fix it.

This happens in the following code:

You can copy-paste this into an empty project and make sure to have Swift 6 enabled under the Build Settings > Swift Language Version

import PhotosUI
import SwiftUI

public struct ContentView: View {
    @State private var avatarItem: PhotosPickerItem?
    @State private var avatarImage: Image?
    @State private var avatarData: Data?

    public var body: some View {
        VStack(spacing: 30) {
            VStack(alignment: .center) {
                PhotosPicker(selection: $avatarItem, matching: .images) {
                    if let avatarImage {
                        avatarImage
                            .resizable()
                            .aspectRatio(contentMode: .fill)
                            .frame(width: 100, height: 100)
                            .foregroundColor(.gray)
                            .background(.white)
                            .clipShape(Circle())
                            .opacity(0.75)
                            .overlay {
                                Image(systemName: "pencil")
                                    .font(.title)
                                    .shadow(radius: 5)
                            }
                    } else {
                        Image(systemName: "person.circle.fill")
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .frame(width: 100, height: 100)
                            .foregroundColor(.gray)
                            .background(.white)
                            .clipShape(Circle())
                            .opacity(0.75)
                            .overlay {
                                Image(systemName: "pencil")
                                    .font(.title)
                                    .shadow(radius: 5)
                            }
                    }
                }
            }
        }
        .onChange(of: avatarItem) {
            Task {
                if let data = try? await avatarItem?.loadTransferable(
                    type: Data.self
                ) {
                    if let processed = processImage(data: data) {
                        avatarImage = processed.image
                        avatarData = processed.data
                    } else {
                        
                    }
                }
            }
        }
    }

    private func processImage(data: Data) -> (image: Image?, data: Data?)? {
        guard let uiImage = UIImage(data: data)?.preparingForDisplay() else {
            return nil
        }

        // Check original size
        let sizeInMB = Double(data.count) / (1024 * 1024)

        // If image is larger than 1MB, compress it
        if sizeInMB > 1.0 {
            guard let compressedData = uiImage.compress() else { return nil }
            return (Image(uiImage: uiImage), compressedData)
        }

        return (Image(uiImage: uiImage), data)
    }
}

#Preview {
        ContentView()
}

public extension UIImage {
    func compress(to maxSizeInMB: Double = 1.0) -> Data? {
        let maxSizeInBytes = Int(
            maxSizeInMB * 1024 * 1024
        ) // Convert MB to bytes
        var compression: CGFloat = 1.0
        let step: CGFloat = 0.1
        var imageData = jpegData(compressionQuality: compression)

        while (imageData?.count ?? 0) > maxSizeInBytes, compression > 0 {
            compression -= step
            imageData = jpegData(compressionQuality: compression)
        }

        return imageData
    }
}
Answered by Frameworks Engineer in 825016022

Since avatarImage is Sendable (because Image is Sendable), you can explicitly capture it in the closure's capture list to make the error go away:

PhotosPicker(selection: $avatarItem, matching: .images) { [avatarImage] in
    ...
}

While the compiler error is confusing, you were seeing it because self (the ContentView), which is not Sendable, was captured implicitly by the trailing closure of PhotosPicker.

Accepted Answer

Since avatarImage is Sendable (because Image is Sendable), you can explicitly capture it in the closure's capture list to make the error go away:

PhotosPicker(selection: $avatarItem, matching: .images) { [avatarImage] in
    ...
}

While the compiler error is confusing, you were seeing it because self (the ContentView), which is not Sendable, was captured implicitly by the trailing closure of PhotosPicker.

SwiftUI Main Actor Isolation Error with PhotosPicker
 
 
Q