Search code examples
iosswiftcloudkitcllocationckrecord

Having trouble retrieving data from CloudKit


I am having trouble fetching the locations from cloudkit. The location gets uploaded, but when I try to have them printed out and loaded, they aren't downloaded. I don't get any errors.

This function uploads the location to CloudKit:

func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation])
    {
        let location = locations.last
        let center = CLLocationCoordinate2D(latitude: location!.coordinate.latitude, longitude: location!.coordinate.longitude)
        let region = MKCoordinateRegion(center: center, span: MKCoordinateSpan(latitudeDelta: 0.015, longitudeDelta: 0.015))
        self.mapView.setRegion(region, animated: true)
        self.locationManager.stopUpdatingLocation()//
        let locationRecord = CKRecord(recordType: "location")
        locationRecord.setObject(location, forKey: "location")
        let publicData = CKContainer.defaultContainer().publicCloudDatabase
        publicData.saveRecord(locationRecord) { record, error in
        }
            if error == nil
            {
                print("Location saved")
            }
        event1 = locations
    }

This function fetches the locations from CloudKit:

func loadLocation()
     {
        let locations = [CKRecord]()
        let publicData1 = CKContainer.defaultContainer().publicCloudDatabase
        let query1 = CKQuery(recordType: "location", predicate: NSPredicate(format: "TRUEPREDICATE", argumentArray:nil))
        publicData1.performQuery(query1, inZoneWithID: nil) { (results: [CKRecord]?, error: NSError?) -> Void in
            if let locations = results
            {
                self.locations = locations
                print(locations)
            }
        }
     }

Solution

  • So to do this I made a unit test, that passes:

    //
    //  CloudKitLocationsTests.swift
    //
    
    import XCTest
    import UIKit
    import CoreLocation
    import CloudKit
    
    class CloudKitLocationsTests: XCTestCase {
    
        let locations = [ CLLocation(latitude: 34.4, longitude: -118.33), CLLocation(latitude: 32.2, longitude: -121.33) ]
    
        func storeLocationToCloud(location:CLLocation) {
            let locationRecord = CKRecord(recordType: "location")
            locationRecord.setObject(location, forKey: "location")
            let publicData = CKContainer.defaultContainer().publicCloudDatabase
            publicData.saveRecord(locationRecord) { (records, error) in
                if error != nil {
                    print("error saving locations: \(error)")
                } else {
                    print("Locations saved: \(records)")
                }
            }
        }
    
        func fetchLocationsFromCloud(completion: (error:NSError?, records:[CKRecord]?) -> Void) {
            let query = CKQuery(recordType: "Location", predicate: NSPredicate(value: true))
            CKContainer.defaultContainer().publicCloudDatabase.performQuery(query, inZoneWithID: nil){
                (records, error) in
                if error != nil {
                    print("error fetching locations")
                    completion(error: error, records: nil)
                } else {
                    print("found locations: \(records)")
                    completion(error: nil, records: records)
                }
            }
        }
    
        func testSavingLocations(){
    
            let testExpectation = expectationWithDescription("saveLocations")
            var n = 0
            for location in self.locations {
                let locationRecord = CKRecord(recordType: "Location")
                locationRecord["location"] = location
                let publicData = CKContainer.defaultContainer().publicCloudDatabase
                publicData.saveRecord(locationRecord) { (records, error) in
                    if error != nil {
                        print("error saving locations: \(error)")
                    } else {
                        print("Locations saved: \(records)")
                    }
                    n += 1
                    if n >= self.locations.count {
                        testExpectation.fulfill()
                    }
                }
            }
    
            // do something then call fulfill (in callback)
    
            waitForExpectationsWithTimeout(10){ error in
                if error != nil {
                    XCTFail("timed out waiting on expectation: \(testExpectation)")
                }
            }
    
        }
    
        func testFetchingLocations(){
            let testExpectation = expectationWithDescription("FetchLocations")
    
            fetchLocationsFromCloud(){ (error, records) in
                if error != nil {
                    XCTFail("error fetching locations")
                } else {
                    XCTAssertGreaterThan(records!.count, 0)
                }
                // do something then call fulfill (in callback)
                testExpectation.fulfill()
            }
    
            waitForExpectationsWithTimeout(10){ error in
                if error != nil {
                    XCTFail("timed out waiting on expectation: \(testExpectation)")
                }
            }
    
        }
    
    
    }
    

    Note that you had case mismatch Location/location. Also, I am doing a subscript to set the field value.

    Run this it works. Getting the location from the location manger callback has nothing to do with CloudKit so you should be able to plug this in as you require.

    One other thing: I did turn on the option to allow you to query on ID field for the Location record type.