CloudKit Error Differentiation - ios

I need some help to learn how to properly handle errors when fetching records via CloudKit. Currently I have an app that saves numerous records in the cloud, and will load them at launch. I have been referencing the records using a CKReference, and anytime I save the reference I use the CKReferenceAction.DeleteSelf option. A problem I've encountered periodically is that when a referenced record is deleted, sometimes there can be a significant amount of time before the reference deletes itself. This has caused me to occasionally come across the situation where my app has fetched a CKReference for a record that no longer exists. I'm able to manually find out when this happens just by inserting print(error!) in my error handler. What I would like to know is how I can add some code to detect this specific error i.e. if error.localizedDescription == ??? {.
Here is the basic code I'm using for the fetch:
let fetch = CKFetchRecordsOperation(recordIDs: recordIDs)
fetch.perRecordCompletionBlock = { (record:CKRecord?, recordID:CKRecordID?, error: NSError?) in
if error != nil {
// Error Line A (See below)
print("ERROR! : \(error!.localizedDescription)")
// Error Line B (See below)
print("ERROR: \(error!)")
}
else if let record = record {
// Record was found
}
}
if let database = self.privateDatabase {
fetch.database = database
fetch.start()
}
And then when it tries to fetch the non-existent record, here is the error message that prints out in the compiler window:
a) ERROR! : Error fetching record <CKRecordID: 0x10025b290; dbbda7c3-adcc-4271-848f-6702160ea34f:(_defaultZone:__defaultOwner__)> from server: Record not found
b) ERROR: <CKError 0x125e82820: "Unknown Item" (11/2003); server message = "Record not found"; uuid = (removed); container ID = "(removed)">
Above in error line B, where it says CKError 0x125e82820:, can I use this to create an if statement to check for this specific error type? I really could use any help finding a way to resolve this issue properly when it happens. I have set up some loading structure for my app, and when it thinks there is a record it needs to find, but can't, it screws up my loading process. I would really appreciate any help I can get, I assume it's an easy solution, but apparently not one I've been able to find. Thank you!
UPDATE -
Thanks to #AaronBrager, I was able to find the correct solution. You can verify the error code to match it to any specific error, and the domain to make sure it's a CKError. Here is the solution that works for me:
let fetch = CKFetchRecordsOperation(recordIDs: recordIDs)
fetch.perRecordCompletionBlock = { (record:CKRecord?, recordID:CKRecordID?, error: NSError?) in
if error != nil {
if error!.code == CKErrorCode.UnknownItem.rawValue && error!.domain == CKErrorDomain {
// This works great!
}
}
else if let record = record {
// Record was found
}
}
if let database = self.publicDatabase {
fetch.database = database
fetch.start()
}

You should be able to uniquely identify an error's cause by inspecting its domain and code variables. Same domain and code, same problem. And unlike localizedDescription, it won't change between users.

Related

CloudKit - How to modify the record created by other user

It was OK in development,
but when I distributed my app by TestFlight, I have had this problem.
While I was checking some failures,
I have guessed that when a user who didn’t create the record try to modify it, can’t do that.
By the way, I can fetch all record values on Public Database. Only modification isn’t performed.
In the picture below, iCloud accounts are written in green areas. I predicted that these have to be same.
image: Metadata - CloudKit Dashboard
Now, users are trying to modify a record by the following code:
func modifyRecord() {
let publicDatabase = CKContainer.default().publicCloudDatabase
let predicate = NSPredicate(format: "accountID == %#", argumentArray: [myID!])
let query = CKQuery(recordType: "Accounts", predicate: predicate)
publicDatabase.perform(query, inZoneWith: nil, completionHandler: {(records, error) in
if let error = error {
print("error1: \(error)")
return
}
for record in records! {
/* ↓ New Value */
record["currentLocation"] = CLLocation(latitude: 40.689283, longitude: -74.044368)
publicDatabase.save(record, completionHandler: {(record, error) in
if let error = error {
print("error2: \(error)")
return
}
print("success!")
})
}
})
}
In development, I created and modified all records by myself, so I was not able to find this problem.
Versions
Xcode 11.6 / Swift 5
Summary
I guessed that it is necessary to create and modify record by same user ( = same iCloud Account ) in this code.
Then, could you please tell me how to modify the record created by other user?
In the first place, can I do that?
Thanks.
Looks like you have a permissions problem. In your cloudkit dashboard, go to: Schema > Security Roles
Then under 'Authenticated' (which means a user logged into Icloud)
you'll need to grant 'Write' permissions to the relevant record type. That should fix it!

check if firebase write operation was successful in iOS

How to check if firebase real time write operation was successful in ios?
I am trying to store data to real time database using the following code but it does not work:-
//adding the artist inside the generated unique key
refArtists.child(key!).setValue(data) { (error, dbreference) in
if error != nil{
print(error?.localizedDescription)
} else {
print("success", dbreference)
}
}
and I also tried following:-
let key = refArtists.childByAutoId().key
//creating artist with the given values
let artist = ["id":key,
"latitude": "34234" as String,
"longitude": "67657" as String
]
let data = ["data": artist]
refArtists.child(key!).setValue(data)
I am unable to understand why my write operation is unsuccessful.
Edit from comments:
The closure does not get called. I am getting this error :
Firebase Database connection was forcefully killed by the server. Will not attempt reconnect. Reason: Firebase error. Please ensure that you spelled the name of your Firebase correctly.

CKRecord fetch return 1 record with empty RecordID

Record from iCloud container is fetched, but there is no RecordID. At the same time in the panel on the site, I see it. I tried to extract the record from another application, I registered the necessary container in the settings - and it was extracted without error.
I do not understand - this is a bug Xcode? After all, the extraction code is identical in the second application, where everything works. And in the debugger at the bottom left you can see that RecordID is not empty.
Code:
privateDatabase.perform(query, inZoneWith: nil) { (results, error) -> Void in
if error != nil {
print(error!.localizedDescription)
result(false)
} else if results != nil, results!.count > 0, let me = results?[0] {
let RN = (me[.recordID] as! CKRecord.ID).recordName
Error:
Thread 2: Fatal error: Unexpectedly found nil while unwrapping an Optional value
Variables View Console:
_recordName __NSCFString * #"UA-kuka-2018-11-16 11:27:59" 0x00000001c0644320
The proper way to get the recordID of a CKRecord is to use the recordID property.
Assuming me is actually a CKRecord:
let RN = me.recordID.recordName

error creating object using Firebase values

I am attempting to pull down information from my Firebase database and use it to create an object of type Order. The error that I have printed in the catch statement is as follows.
Error Domain=myProjectName.OrderError Code=0 "(null)"
I am unsure what this means exactly, or how to fix it.
I have defined a custom error type in my Order class, as shown below.
enum OrderError: ErrorType
{
case IllegalOrderNumber
case InvalidEntry
}
The error is generated by the following code snippet.
self.ref.child("orders").observeEventType(.ChildAdded, withBlock: { (snapshot) in
let pickupLoc = snapshot.value!["pickupLocation"] as? String
let dropoffLoc = snapshot.value!["dropoffLocation"] as? String
let orderNumInt = snapshot.value!["orderNum"] as? Int
//since the database will return nil if you try and cast a string to an int
//we get it as an int then cast to string
let orderNum = String(orderNumInt)
do
{
let myOrder = try Order(PickUpLoc: pickupLoc, DropOffLoc: dropoffLoc, OrderNum: orderNum)!
self.orders.append(myOrder)
}
catch let error as NSError
{
//should never get here
print(error)
}
})
I do all of the error checking when the user enters the value into the database, so there should be no reason for there to be an error generated.
Okay, so after combing through my code I noticed two primary possible error sources. Firstly, despite the compiler not complaining, the do-catch block was not exhaustive, and therefore an additional catch was added to fix this. Secondly, I believe the error was generated by the fact that I declared my Orders array as var orders = [Order()] as opposed to var orders = [Order](), when I changed this, the program ran smoothly.

NSInvalidArgumentException when trying to retrieve images

I am getting an error using a "properly working" code in another place:
[Error]: Caught "NSInvalidArgumentException" with reason "*** -[_NSPlaceholderData
initWithContentsOfFile:options:error:]: nil file argument"
I declared an array of arrays group:[[AnyObject]]
In my CellForRowAtIndexPath method in my UITableView I am starting the following query based on an array which is an element of group => group[indexPath.row].
I can get the necessary data without a problem, but when I try to use my getDataInBackgroundWithBlock() method, it throws the error above.
var memberPhotoImages:[UIImage] = [UIImage]()
let buttonImageQuery = PFUser.query()
buttonImageQuery?.whereKey("objectId", containedIn: group[indexPath.row])
buttonImageQuery?.findObjectsInBackgroundWithBlock({ (results, error) -> Void in
if error != nil {
print(error)
} else {
self.memberPhotosFiles.removeAll()
if let results = results {
for result in results {
let buttonPicture = result["firstImage"] as! PFFile
self.memberPhotosFiles.append(buttonPicture)
buttonPicture.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error != nil {
//...
} else {
if let data = imageData {
print("success!")
}}}}}}})
return cell
}
Any ideas how to solve this? tried a lot of typecasting so far, but it must be something else.
edit: It prints "Success!", but also the error msg.
I had to guess from the error, it looks like you're trying to access a file that doesn't exist on the device -[_NSPlaceholderData initWithContentsOfFile:options:error:]: nil file argument The issue could be that you're accessing something from a place where the simulator can get to it (possibly inside the iOS Simulator's files, which are in Application Support,) but the device cannot. Check your code for any hard-coded paths that might lead to a place on your computer rather than accessing the device's filesystem.
It show that your file argument is nil. Most of such cases are due to different file path between simulator and device. Check your code about loading file and compare the path between simulator and device.

Resources