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)
}
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?
Here is the code when a button is tapped.
#IBAction func sendTapped(_ sender: Any) {
let message = PFObject(className: "Message")
message["sender"] = PFUser.current()?.objectId
message["recipient"] = recipientObjectId
message.addUniqueObject(messageTextField.text, forKey: "messages")
message.saveInBackground(block: { (success, error) in
if success {
print("did it save?")
}
})
}
Everytime the button is pressed, it creates a new Objectid in the parse server dashboard. How do I make it so it uses the same ObjectId, and messages keep adding to the messages array?
Thanks,
Updating Objects
Updating an object is simple. Just set some new data on it and call
one of the save methods. Assuming you have saved the object and have
the objectId, you can retrieve the PFObject using a PFQuery and update
its data:
var query = PFQuery(className:"GameScore")
query.getObjectInBackgroundWithId("xWMyZEGZ") {
(gameScore: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let gameScore = gameScore {
gameScore["cheatMode"] = true
gameScore["score"] = 1338
gameScore.saveInBackground()
}
More here in Parse-update-Docs
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?
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.
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)
}
}