How can i pass sortDescriptors into CloudKitUtility.fetch() as parameter in @MainActor environment?

112 Views Asked by At
Non-sendable type '[NSSortDescriptor]?' exiting main actor-isolated context in call to non-isolated static method 'fetch(recordType:predicate:sortDescriptions:resultsLimit:)' cannot cross actor boundary

Following code will report this error with @MainActor.

NSSortDescriptor is NSObject, '[NSSortDescriptor]?' is Non-sendable type, CloudKitUtility.fetch() is non-isolated static method, so we cannot pass sortDescriptors into CloudKitUtility.fetch() as parameter in @MainActor environment?

How can I fix this?

    @MainActor
    func fetch() async {
        let predicate = NSPredicate(value: true)
        let sortDescriptors: [NSSortDescriptor] = []
        print("fetch...")
        await MainActor.run {
            self.isLoading = true
        }
        do {
            let (items, cursor): ([Video], CKQueryOperation.Cursor?) = try await CloudKitUtility.fetch(recordType: Video.recordTypeName, predicate: predicate, sortDescriptions: sortDescriptors, resultsLimit: 100)
//            await MainActor.run {
                self.videos = items
                self.cursor = cursor
                self.isLoading = false
                print("fetch done, total = \(videos.count), has more items = \(self.hasMore)")
//            }
        } catch {
//            await MainActor.run {
                self.isLoading = false
//            }
            print(error.localizedDescription)
        }
    }
1

There are 1 best solutions below

3
On

Give this a try:

    func fetch() async throws {
        // background thread
        let predicate = NSPredicate(value: true)
        let sortDescriptors: [NSSortDescriptor] = []
        print("fetch...")
        let (items, cursor): ([Video], CKQueryOperation.Cursor?) = try await CloudKitUtility.fetch(recordType: Video.recordTypeName, predicate: predicate, sortDescriptions: sortDescriptors, resultsLimit: 100)
         Task { @MainActor in
             // main thread
             self.videos = items
             self.cursor = cursor
             print("fetch done, total = \(videos.count), has more items = \(self.hasMore)")
//      }
    }

Use like this:

@StateObject private var fetcher = Fetcher()
@State private var message = ""
@State private var loading = false
        
var body: some View {
    Button(loading ? "Cancel" : "Load") {
        loading.toggle()
    }
    .task(id: loading) {
         if !loading {
             return
         }
         do {
              await fetcher.fetch()
              message = ""
         }
         catch {
             message = error.localizedDescription
         }
         loading = false
     }
}

You can see this pattern in Apple's sample code for Meme Creator here.