Search code examples
swiftcloudkitswiftui

How to retrieve data from CloudKit using SwiftUI


I am using SwiftUI to make an app and storing the data on ICloud. Because all the code I can find relates to Swift and viewDidLoad and TableView, however this do not apply. I have written code and seems to retrieve it but will not return it to the ObservableObject to be able to display in the SwiftUI

The ObservableObject file:-

import SwiftUI
import Combine

final class ObservedData: ObservableObject {
    @Published var venues = venuesData
}

The query to retrieve data

import SwiftUI
import CloudKit

var venuesData: [Venue] = loadVenues()

func loadVenues() -> [Venue] {

    var data = [Venue]()

    let pred = NSPredicate(value: true)
    let sort = NSSortDescriptor(key: "id", ascending: true)
    let query = CKQuery(recordType: "DeptfordHopData", predicate: pred)
    query.sortDescriptors = [sort]

    let operation = CKQueryOperation(query: query)
    operation.desiredKeys = ["id","name", "address"]
    operation.resultsLimit = 50

    var newVenues = [Venue]()

    operation.recordFetchedBlock = { record in
        let venue = Venue()
        venue.racordID = record.recordID
        venue.id = record["id"]
        venue.name = record["name"]
        venue.address = record["address"]
        newVenues.append(venue)
    }

    operation.queryCompletionBlock = {(cursor, error) in
        DispatchQueue.main.async {
            if error == nil {
                data = newVenues
            } else {
                print(error!.localizedDescription)
            }
        }
    }

    CKContainer.default().publicCloudDatabase.add(operation)

    return data
}

I have got data showing when do break in data but does not pass to venueData


Solution

  • The query operation runs asynchronously. When you add the operation to the database, the framework runs the query in the background and immediately returns control to your function, which promptly returns data, which is still set to the empty array.

    To fix this, you either need to wait for the operation to complete using Grand Central Dispatch (not recommended if this is called directly from UI code, as it will block your application), or you need to rewrite loadVenues so it accepts a callback that you can then invoke inside queryCompletionBlock. The callback you provide to loadVenues should save the array of venues somewhere where SwiftUI will detect the change.