My code looks like this and there’s a concurrency warning when accessing the loadTransferable
for the selectedPhoto
from PhotosPicker
struct SampleCode: View {
@State var showOptionSheet = false
@State var showPhotoPicker = false
@State var selectedPhoto: PhotosPickerItem?
@State var showProgressView = false
var body: some View {
Button {
showOptionSheet = true
} label: {
CustomAvatar(
imageURL: guestProfile.avatarUrl,
width: 40,
height: 40
)
}.confirmationDialog("Upload new Avatar", isPresented: $showOptionSheet, titleVisibility: .visible, actions: {
Button {
showPhotoPicker = true
} label: {
Text("Photos Library")
}
})
.photosPicker(isPresented: $showPhotoPicker, selection: $selectedPhoto, matching: .images)
.task(id: selectedPhoto) {
showProgressView = true
if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { // WARNING: Passing argument of non-sendable type 'PhotosPickerItem' outside of main actor-isolated context may introduce data races
let result = await uploadUserAvatar(data: data)
if let result {
guestProfile.avatarUrl = result
}
}
showProgressView = false
}
}
}
We recently set the concurrency warnings to complete and I’m not really sure what to do with this
I tried to make PhotosPickerItem conform to Sendable but it produces a different warning and I’m not sure if this is safe
extension PhotosPickerItem: Sendable {} // Conformance to 'Sendable' must occur in the same source file as struct 'PhotosPickerItem'; use '@unchecked Sendable' for retroactive conformance
What’s the best way to solve this?
2
Answers
This will work if you move the code inside a
.onChange
modifier instead and useTask
Here we don’t pass selectedPhoto outside the main actor instead we have a constant local variable
photo
that we pass to the task.You can tell your
task
view modifier to use its own copy ofselectedPhoto
with a capture list: