Swift 6:错误:无法发送任务或参与者隔离值

问题描述 投票:0回答:1

我正在尝试解决编译错误:

Task or actor isolated value cannot be sent
在使用带有 Swift 6 的 Xcode 16.0 beta 3 和
Complete
启用并发时。我的代码:

import SwiftUI
@preconcurrency import Contacts
import ContactsUI

struct SimpleContactPicker: View {
    
    let allowedContactType: CNContactType? = CNContactType.person
    
    @Binding var selectedContacts: [CNContact]?

    var body: some View {
        SimpleContactPickerProxy(allowedContactType: self.allowedContactType, selectedContacts: $selectedContacts)
            .ignoresSafeArea()
    }
}

extension CNContactType {
    
    var predicateForEnablingContact: NSPredicate {
        
        NSPredicate(format: "contactType=\(self.rawValue)")
    }
}

struct SimpleContactPickerProxy: UIViewControllerRepresentable, @unchecked Sendable {
    
    typealias UIViewControllerType = CNContactPickerViewController
    
    let allowedContactType: CNContactType?
    
    @Binding var selectedContacts: [CNContact]?

    final class Coordinator: NSObject, CNContactPickerDelegate, @unchecked Sendable {
        var parent: SimpleContactPickerProxy

        init(_ parent: SimpleContactPickerProxy) {
            self.parent = parent
        }
        
        func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
        }

        func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
           
            Task { @MainActor in
                self.parent.selectedContacts = contacts // <<-- ERROR: Task or actor isolated value cannot be sent
            }
        }
    }
    
    func makeUIViewController(context: Context) -> CNContactPickerViewController {
        
        let contactPickerViewController = CNContactPickerViewController()
        
        contactPickerViewController.delegate = context.coordinator
        
        if let allowedContactType {
            contactPickerViewController.predicateForEnablingContact = allowedContactType.predicateForEnablingContact
        }
        
        return contactPickerViewController
    }
    
    func updateUIViewController(_ uiViewController: CNContactPickerViewController, context: Context) {
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}

虽然我通常理解

[CNContact]
数组无法从非隔离上下文发送到
MainActor
上下文,因为
CNContact
是不可发送的,但目前还不清楚如何解决这个问题。 (这个问题在早期的 Xcode 16 beta 中没有出现)。

我想可以用复制的数据创建我自己的

CNContact
传真结构,但这似乎是一笔令人讨厌的开销。我还尝试了
MainActor.assumeIsolated
以及 WWDC 2024 Swift Concurrency 视频中讨论的其他策略 https://developer.apple.com/wwdc24/10169,但这些方法似乎都不起作用。

如有任何帮助,我们将不胜感激。

swift swiftui concurrency swift6
1个回答
0
投票

首先您应该知道哪些类型可以安全地声明为

Sendable

值类型

没有可变存储的引用类型

内部管理对其状态的访问的引用类型

函数和闭包(通过用 @Sendable 标记它们)

CNContact
标记为
Sendable
就可以了

CNContact 对象存储联系人信息的不可变副本,因此您无法直接更改此对象中的信息。联系人对象是线程安全的,因此您可以从应用程序的任何线程访问它们。

请注意,

View
Coordinator
可以通过
@MainActor
变得安全,它们不需要
Sendable

import SwiftUI
import Contacts
import ContactsUI

struct ContactsView: View {
    @State private var selected: [CNContact] = []
    var body: some View {
        SimpleContactPicker(selectedContacts: $selected)
    }
}

#Preview {
    ContactsView()
}



struct SimpleContactPicker: View {
    
    let allowedContactType: CNContactType? = CNContactType.person
    
    @Binding var selectedContacts: [CNContact]

    var body: some View {
        SimpleContactPickerProxy(allowedContactType: self.allowedContactType, selectedContacts: $selectedContacts)
            .ignoresSafeArea()
    }
}

struct SimpleContactPickerProxy: UIViewControllerRepresentable {
    
    typealias UIViewControllerType = CNContactPickerViewController
    
    let allowedContactType: CNContactType?
    
    @Binding var selectedContacts: [CNContact]
    
    @MainActor
    final class Coordinator: NSObject, CNContactPickerDelegate {
        var parent: SimpleContactPickerProxy

        init(_ parent: SimpleContactPickerProxy) {
            self.parent = parent
        }
        
        nonisolated func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
        }

        nonisolated func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
           
            Task { @MainActor in
                parent.selectedContacts = contacts
            }
        }
    }
    
    func makeUIViewController(context: Context) -> CNContactPickerViewController {
        
        let contactPickerViewController = CNContactPickerViewController()
        
        contactPickerViewController.delegate = context.coordinator
        
        if let allowedContactType {
            contactPickerViewController.predicateForEnablingContact = allowedContactType.predicateForEnablingContact
        }
        
        return contactPickerViewController
    }
    
    func updateUIViewController(_ uiViewController: CNContactPickerViewController, context: Context) {
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
}


extension CNContactType {
    
    var predicateForEnablingContact: NSPredicate {
        
        NSPredicate(format: "contactType=\(self.rawValue)")
    }
}

extension CNContact: @unchecked @retroactive Sendable {}
© www.soinside.com 2019 - 2024. All rights reserved.