How to delete a CKRecord - ios

How do I delete a CKRecord from my database programmatically, on the client side.
for record in records {
if recordNumber < 13 {
let moodRecord:CKRecord = record as! CKRecord
self.moodArray.append(moodRecord.objectForKey("Color") as! String)
}
else if recordNumber > 13 {
// DELETE RECORD HERE
record.delete(CKRecord)
recordNumber--
}
recordNumber++
}

That's not how you delete records in CloudKit. Here's how I delete records in my app:
let operation = CKModifyRecordsOperation(recordsToSave: nil, recordIDsToDelete: [record.recordID])
operation.savePolicy = .AllKeys
operation.modifyRecordsCompletionBlock = { added, deleted, error in
if error != nil {
println(error) // print error if any
} else {
// no errors, all set!
}
}
CKContainer.defaultContainer().publicCloudDatabase.addOperation(operation)

You can use
delete(withRecordID:completionHandler:)
apple docs
objc version:
[aDatabase deleteRecordWithID:rec.recordID completionHandler:^(CKRecordID * _Nullable recordID, NSError * _Nullable error) {
;
}];

let database = CKContainer.default().publicCloudDatabase // or privateCloudDatabase
let recordID = someRecord.recordID
database.delete(withRecordID: recordID) { (ckRecordID, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let id = ckRecordID else {
return
}
print(id)
}

Related

Records, Zone doesn't displayed in Dashboard and Delete Zone issue CloudKit

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?

Manually Throw a CKErrorPartialFailure

I'm tracking down a CloudKit error of 'Failed to modify some records.'
How can I throw this error so that I can test my error handling code?
Is there a property of CKRecord I can set to force it to fail?
Code is currently something like:
var someRecords = [CKRecord]()
for i in (1...10) {
let record = CKRecord(recordType: "Track", recordID: CKRecord.ID(zoneID: recordZone.zoneID))
...
someRecords.append(record)
}
let operation = CKModifyRecordsOperation(recordsToSave: someRecords, recordIDsToDelete: nil)
operation.modifyRecordsCompletionBlock = { savedRecords, deletedRecords, error in
DispatchQueue.main.async {
if !self.handleError(error) { ... }
}
}
You need an Atomic zone (as I remember)
Try to save a big amount of objects (as I remember: it will allow you to use no more < 500)
I hope this will help you
var someRecords = [CKRecord]()
for i in (1...1000) {
let record = CKRecord(recordType: "Track", recordID: CKRecord.ID(zoneID: recordZone.zoneID))
...
someRecords.append(record)
}
let operation = CKModifyRecordsOperation(recordsToSave: someRecords, recordIDsToDelete: nil)
operation.isAtomic = true
operation.modifyRecordsCompletionBlock = { savedRecords, deletedRecords, error in
DispatchQueue.main.async {
if !self.handleError(error) { ... }
}
}

Can't save to Cloudkit public database after saving to custom zone in private database

Good evening,
I have run into the issue of getting the following error message from Cloudkit when trying to save a record to the public database:
"Server Rejected Request" (15/2027); server message = "Custom zones are not allowed in public DB";
I have been able to find the issue causing this error. Within this application I need to fetch zone changes on the private database so I have had to save my records to a custom zone in order to accomplish that fetch.
The following code is where I am saving to the custom zone on the private database:
let zoneID = CKManager.defaultManager.sharedZone?.zoneID
let deckSetToBeSaved = deckName.deckToCKRecord(zoneID)
privateDatabase.save(deckSetToBeSaved) { (record, error) in
DispatchQueue.main.async {
if let record = record {
deckName.cKRecordToDeck(record)
try? self.managedContext.save()
}
}
print("New record saved")
print(error as Any)
}
}
Here is my code for saving a record to the public database:
func shareDeckPlan(_ deck: Deck, completion: #escaping (Bool, NSError?) -> Void ) {
var records = [CKRecord]()
let deckRecord = deck.deckToCKRecord()
records.append(deckRecord)
let reference = CKReference(record: deckRecord, action: .deleteSelf)
for index in deck.cards {
let cardRecord = index.cardToCKRecord()
cardRecord["deck"] = reference
records.append(cardRecord)
}
let operation = CKModifyRecordsOperation(recordsToSave: records, recordIDsToDelete: nil)
operation.modifyRecordsCompletionBlock = {(savedRecords, deletedIDs, error) in
if error == nil {
DispatchQueue.main.async(execute: {
if let savedRecords = savedRecords {
for record in savedRecords {
if record.recordID.recordName == deck.ckRecordID {
deck.cKRecordToDeck(record)
}
for cards in deck.cards {
if record.recordID.recordName == cards.ckRecordID {
cards.ckRecordToCard(record)
}
}
}
}
let _ = try? self.managedContext.save()
DispatchQueue.main.async(execute: {
completion(true, error as NSError?)
})
})
} else {
DispatchQueue.main.async(execute: {
print(error!)
completion(false, error as NSError?)
})
}
}
operation.qualityOfService = .userInitiated
self.publicDatabase.add(operation)
}
The following code referes to the deckToCKRecord method:
func deckToCKRecord(_ zoneID:CKRecordZoneID? = nil) -> CKRecord {
let deckSetName: CKRecord
if let ckMetaData = ckMetaData {
let unarchiver = NSKeyedUnarchiver(forReadingWith: ckMetaData as Data)
unarchiver.requiresSecureCoding = true
deckSetName = CKRecord(coder: unarchiver)!
}
else {
if let zoneID = zoneID {
deckSetName = CKRecord(recordType: "Deck", zoneID: zoneID)
self.ckRecordID = deckSetName.recordID.recordName
} else {
deckSetName = CKRecord(recordType: "Deck")
self.ckRecordID = deckSetName.recordID.recordName
}
}
deckSetName["name"] = name! as CKRecordValue
deckSetName["cardCount"] = cards.count as CKRecordValue
return deckSetName
}
How can I save a record both to the private database in a custom zone and the public database successfully?

Add unique values to my array in Parse Server (swift 3)

My Parse Server has a class called "BooksAddedByUser", which has 3 columns:
objectId
user - contains the username of the PFUser.current()
ISBNArray - the ISBN of books added by the user
I would like to add the newly added bookNumber into the ISBNArray only if it doesn't exist in the that array yet. However, whenever I run my code below, it creates new objectIds with the same username in the class "BooksAddedByUser". And it also doesn't check if the bookNumber already exists. I'm not sure what's going on honestly.
let booksAddedByUser = PFObject(className: "BooksAddedByUser")
booksAddedByUser["user"] = PFUser.current()?.username
let query = PFQuery(className: "BooksAddedByUser")
query.whereKey("ISBNArray", contains: bookNumber)
query.findObjectsInBackground { (objects, error) in
if error != nil {
print(error)
} else {
print(self.bookNumber)
if let objects = objects {
for object in objects {
print("book is already added i think")
}
}
}
}
booksAddedByUser.addObjects(from: [bookNumber], forKey: "ISBNArray")
booksAddedByUser.saveInBackground { (success, error) in
if error != nil {
print("error saving new book")
} else {
print("new book saved!")
}
}
EDIT w/ new code:
let booksAddedByUser = PFObject(className: "BooksAddedByUser")
booksAddedByUser["user"] = PFUser.current()?.username
let query = PFQuery(className: "BooksAddedByUser")
query.findObjectsInBackground { (objects, error) in
if error != nil {
print(error)
} else {
print(self.bookNumber)
if let objects = objects {
if objects.contains(bookNumber) {
print("book exists")
}
}
}
}
booksAddedByUser.addObjects(from: [bookNumber], forKey: "ISBNArray")
booksAddedByUser.saveInBackground { (success, error) in
if error != nil {
print("error saving new book")
} else {
print("new book saved!")
}
}
You can check if an object exits in array by using this:
if arrObjects.contains(where: { $0.bookNumberPropertyName == "bookNumber" }) {
print("Book exists")
}

Issue update a CKRecord

I can't save a record, which is already saved. recordToSave is a CKRecord download from the server. Is it possible to update it?
recordTosave.setValue("cat", forKey: "animal")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.saveRecord(recordToSave, completionHandler:{(record: CKRecord?, error: NSError?) in
if error == nil{
}else{
print(error.debugDescription)
}
})
You cannot insert record which is already existing in cloudkit. You can modify the record using CKModifyRecordsOperation. Fetch the record using record ID and then update through modify operation.
let recordIDToSave = CKRecordID(recordName: "recordID")
let publicData = CKContainer.defaultContainer().publicCloudDatabase
publicData.fetchRecordWithID(recordIDToSave) { (record, error) in
if let recordToSave = record {
//Modify the record value here
recordToSave.setObject("value", forKey: "key")
let modifyRecords = CKModifyRecordsOperation(recordsToSave:[recordToSave], recordIDsToDelete: nil)
modifyRecords.savePolicy = CKRecordSavePolicy.AllKeys
modifyRecords.qualityOfService = NSQualityOfService.UserInitiated
modifyRecords.modifyRecordsCompletionBlock = { savedRecords, deletedRecordIDs, error in
if error == nil {
print("Modified")
}else {
print(error)
}
}
publicData.addOperation(modifyRecords)
}else{
print(error.debugDescription)
}
}

Resources