I am saving a record to a public database in an app that is currently under external testing.
One of my testers has a problem that I can seem to work out - she saves the record, which the program thinks saves currently, and throws no errors, but the record doesn't show up on iCloud, and we cannot retrieve the record.
Other testers have not had this issue - only her. Her iCloud and iCloud drive is turned on and enabled for the app.
The code too save the record:
let newRecord = CKRecord(recordType: "Club")
loadedRecord = newRecord
publicDB.save(newRecord) { (nil, error) in
if error != nil {
dispError = String(describing: error)
print("Create Club Error = \(String(describing: error))")
} else {
//dispError = nil
clubCreated[0] = true
}
}
The code to retrieve the record:
let namePredicate = NSPredicate(format: "self contains '\(predSearchTerm)'")
print(predSearchTerm)
let query = CKQuery(recordType: ClubType, predicate: namePredicate)
publicDB.perform(query, inZoneWith: nil) { [unowned self] results, error in
if error != nil {
DispatchQueue.main.async {
dispError = String(describing: error)
print("Cloud Query Error - Fetch Clubs: \(dispError)")
}
return
}
//dispError = nil
self.items.removeAll(keepingCapacity: true)
results?.forEach({ (record: CKRecord) in
loadedRecord = results?[0]
self.items.append(Club(record: record, database: self.publicDB))
})
clubLoaded = true
}
When I check to see the number of items retrieved it says 0, and the record cannot be found on iCloud.
Related
I have created a zone for privateCloudDatabase.
static var privateCloudDatabase: CKDatabase {
let container = CKContainer(identifier: "iCloud.<bundle>")
return container.privateCloudDatabase
}
static func createZone() {
let fetchZonesOperation = CKFetchRecordZonesOperation.fetchAllRecordZonesOperation()
fetchZonesOperation.fetchRecordZonesCompletionBlock = {
(recordZones: [CKRecordZone.ID : CKRecordZone]?, error: Error?) -> Void in
guard error == nil else {
return
}
for recordID in recordZones.keys {
if recordID.zoneName == zoneName {
print("Zone Already Created: \(recordID)")
} else if recordID.zoneName == "_defaultZone" {
print("Deafult Zone")
} else {
let customZone = CKRecordZone(zoneName: zoneName)
privateCloudDatabase.save(customZone) { zone, error in
if let error = error{
print("Zone creation error: \(String(describing: error))")
} else {
print("Zone created: \(String(describing: zone?.zoneID.zoneName))")
}
}
}
}
}
fetchZonesOperation.qualityOfService = .utility
privateCloudDatabase.add(fetchZonesOperation)
}
It works successfully and I got a success message but created Zone doesn't display in CloudKit Dashboard. It only shows the _default zone as of now.
The other issue is related to delete all data from Zone. For that, I used below code
let fetchZonesOperation = CKFetchRecordZonesOperation.fetchAllRecordZonesOperation()
fetchZonesOperation.fetchRecordZonesCompletionBlock = {
(recordZones: [CKRecordZone.ID : CKRecordZone]?, error: Error?) -> Void in
guard error == nil else {
return
}
guard let recordZones = recordZones else { return }
let deletionOperation = CKModifyRecordZonesOperation(recordZonesToSave: nil, recordZoneIDsToDelete: recordZones.keys.map { $0 })
deletionOperation.modifyRecordZonesCompletionBlock = { _, deletedZones, error in
guard error == nil else {
let error = error!
print("Error deleting records.", error)
return
}
print("Records successfully deleted in this zone.")
}
}
fetchZonesOperation.qualityOfService = .userInitiated
privateCloudDatabase.add(fetchZonesOperation)
Here I neither get any success message not get any error message. The other method I tried to delete all data from the zone is
let customZone = CKRecordZone(zoneName: zoneName)
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: recordType, predicate: predicate)
privateCloudDatabase.perform(query, inZoneWith: customZone.zoneID) { (records, error) in
if error == nil {
for record in records! {
removeRecord(record.recordID.recordName) { record, error in
}
}
}
}
Here, I get the success message but when I am trying to fetch data from the zone, I get all entries and that suggests data aren't deleted using any of the above methods. Any suggestions for these queries?
Whenever I have run into inexplicable errors and the absence of data updates in CloudKit, it was usually because of something silly like:
A typo in the container identifier so it was interacting with the wrong database on CloudKit.
The account on my Apple device that I was using was different than the account I was signing into in the CloudKit Dashboard.
Have you checked the CloudKit logs to confirm that these actions are hitting your database?
I'm having trouble finding a recordName String that will find any CKRecord depending on the user. I want to fetch the record, modify it, and then save it back to the cloud. I can do this but with only one record at a time. If i switch users my project crashes because the recordName: doesn't work for the new user.
if userLoad() {
let newInfo = CKRecordID(recordName: "212F4229-67B3-469D-AF4A-B9A693512AD2")
database.fetch(withRecordID: newInfo) { (record, Error) in
record?["username"] = self.textField1.text as CKRecordValue?
record?["type"] = self.textField2.text as CKRecordValue?
record?["hours"] = self.textField3.text as CKRecordValue?
self.database.save(record!, completionHandler: { (record, error) in
if error != nil{print("error on info")
} else {print("saved new info")}
})
}} else {print("no user")}
self.performSegue(withIdentifier: "account4", sender: self)
}
I am fairly new to Swift, and just started learning about CloudKit this week for an iOS app project.
The database is working, I can add records and find records in the database.
I have run into a problem sending a query to CloudKit and accessing the data related to the query.
The CloudKit data has unique identifiers, so the search is for one of those identifiers (so a query should only return one record). I am then trying to obtain three pieces of information from that record - "UPC", "foodName", and "Ingredients" (all strings)
Here is the code section that executes the query and tries to obtain the data.
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
let predicate = NSPredicate(format: "UPC = %#", subStr)
let query = CKQuery(recordType: "Food", predicate: predicate)
publicDatabase.performQuery(query, inZoneWithID: nil,
completionHandler: ({results, error in
if (error != nil) {
dispatch_async(dispatch_get_main_queue()) {
print("CloudKit Error")
}
} else {
if results!.count > 0 {
var record = results as! CKRecord
dispatch_async(dispatch_get_main_queue()) {
print("UPC Found")
let cloudUPC = record.objectForKey("UPC") as! CKAsset
print("UPC from CloudKit \(cloudUPC)")
}
} else {
dispatch_async(dispatch_get_main_queue()) {
print("UPC Not Found")
}
}
}
}))
The crash occurs at this point
var record = results as! CKRecord
and returns "EXC_Breakpoint(code = 1, subcode - 0x10047b7c)
Any suggestions on how to solve this problem?
Thanks
Thank you for the suggestion rmaddy.
This is how I fixed the code
let publicDatabase = CKContainer.defaultContainer().publicCloudDatabase
let predicate = NSPredicate(format: "UPC = %#", subStr)
let query = CKQuery(recordType: "Food", predicate: predicate)
publicDatabase.performQuery(query, inZoneWithID: nil,
completionHandler: ({results, error in
if (error != nil) {
dispatch_async(dispatch_get_main_queue()) {
print("Cloud Error")
}
} else {
if results!.count > 0 {
dispatch_async(dispatch_get_main_queue()) {
for entry in results! {
let cloudUPC = entry["UPC"] as? String
print("UPC from CloudKit \(cloudUPC)")
let cloudFoodName = entry["foodName"] as? String
print("Name from CloudKit \(cloudFoodName)")
let cloudIngredients = entry["Ingredients"] as? String
print("Ingredients from CloudKit \(cloudIngredients)")
}
} else {
dispatch_async(dispatch_get_main_queue()) {
print("UPC Not Found")
}
}
}
}))
you need to first to write for loop on items in record then print each UPC
like that :
for item in record {
print(item["UPC"]
print(item["foodName"]
}
Scenario: I created a cloud kit container & record in my dashboard.
But I can't access it via code.
1) Here's my bundle identifier:
2) I setup CloudKit in Xcode:
3) I clicked the 'CloudKit Dashboard' button, went to the corresponding web page and added a record to be fetched:
The I attempted to extract that data programmatically; and failed:
#IBAction func retrieveRecords(sender: UIButton) {
let myContainer = CKContainer.defaultContainer()
let publicDatabase = myContainer.publicCloudDatabase
let predicate = NSPredicate(value: true)
let myQuery = CKQuery(recordType: "Students", predicate: predicate)
publicDatabase.performQuery(myQuery, inZoneWithID: nil) { (record, error) -> Void in
if (nil != error) {
print("Error: \(error!.localizedDescription)")
} else {
print("The Received Record: \(record)")
}
}
}
I only get the following error:
Error: did not find required record type: Students
This should be a simple method with results but apparently, my code doesn't recognize the database to function. What am I missing?
I am using the code below to update a record. Once the record has been updated I would like to run my refresh function. At the moment the refresh function is sometimes called before the record has been updated so the refresh results are the same as before the record was updated.
Thanks
var tempDocumentsArray:NSArray!
let recordID = CKRecordID(recordName: "layerAbove")
var predicate = NSPredicate(format: "recordID = %#", recordID)
let query = CKQuery(recordType: "Layers", predicate: predicate)
self.publicDB.performQuery(query, inZoneWithID: nil) { (results, error) -> Void in
tempDocumentsArray = results
print("Results are: \(tempDocumentsArray)")
let record = tempDocumentsArray[0] as! CKRecord
var layerAbovePrevPos = record.objectForKey("layerNumber") as! Int
layerAbovePrevPos = layerAbovePrevPos - 1
let nlnChanged = record.setObject(layerAbovePrevPos, forKey: "layerNumber")
self.publicDB.saveRecord(record, completionHandler: { (returnRecord, error) -> Void in
if let err = error {
print("Error: \(err.localizedDescription)")
} else {
dispatch_async(dispatch_get_main_queue()) {
print("Success")
//TODO:This is sometimes called before the save is complete!
self.resetAndGet()
}
}
})
}