App crashes when saving a dictionary object in Firestore - ios

I was trying to save my quiz data into firestore like so
db.collection("Quizzes").addDocument(data: ["Author": userEmail,
"quizTitle": quizTitle,
"quizDescription": quizDescription,
"quizDictionary": quizDictionary]) { (error) in
if let e = error{
print("There was an erorr saving the data to the Firestore \(e)")
}
after clicking the button that would perform this block of code my app crashes and the following error is printed
020-09-19 17:17:46.367783+0800 Quizzler[18001:782362] -[__NSCFNumber length]: unrecognized selector sent to instance 0xc4cd28a1a98b5156
2020-09-19 17:17:46.469546+0800 Quizzler[18001:782362] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber length]: unrecognized selector sent to instance 0xc4cd28a1a98b5156'
After doing some debugging, I found out that the "quizDictionary": quizDictionary part is what's causing the crash. Why is this happening? I thought that the method .addDocument(data: ) accepts [String: Any]?
If it is impossible to save Dictionary object into firestore, what are some workarounds for this?
If needed, this is my dictionary's declaration
dictionary declaration

Hey have a look here at the documentation for adding and managing data in fireStore :- https://cloud.google.com/firestore/docs/manage-data/add-data#swift.
Quick Overview line :- Using Map or Dictionary objects to represent your documents is often not very convenient, so Firestore supports writing documents with custom classes. Firestore converts the objects to supported data types.
Update :- As mentioned in comments it does not just convert any random object into a supported type. If you throw an NSImage at it, it won't know what to do with that. Or if the object contains some other random object it won't work. It has to contain supported types only. You can refer to this link to understand more about the supported data types.

You can only read and write supported data types with Firestore:
arrays
booleans
bytes
Firestore date objects
floating-point numbers
Firestore geographical objects
integers
maps (dictionaries, with string keys and values only of these supported types)
nil
Firestore references
strings
Therefore, you must convert your custom object to a format that is recognizable to Firestore which means replacing custom types with types that are primitive to the database.
https://firebase.google.com/docs/firestore/manage-data/data-types

Related

Crash when using error.localizedDescription from completion

I'm trying to check if error.localizedDescription contains a certain string but i keep getting a crash
if error.localizedDescription.contains("\"api.error.cardRejected.2000\"") {
failCompletion()
}
I have even tried to even use another way
if let description = (error! as NSError).userInfo[NSLocalizedDescriptionKey] as? String {
if description.contains("api.error.cardRejected.2000") {
failCompletion()
}
}
I still keep getting the same crash in the logs saying
-[__NSDictionaryM domain]: unrecognized selector sent to instance 0x60000046b520
It works when i check using the debugDescription but i would like to check using the localizedDecription since the debug one only works when debugging
NSError localized description is autogenerated from what's inside, here is what API tells:
/* The primary user-presentable message for the error, for instance for NSFileReadNoPermissionError: "The file "File Name" couldn't be opened because you don't have permission to view it.". This message should ideally indicate what failed and why it failed. This value either comes from NSLocalizedDescriptionKey, or NSLocalizedFailureErrorKey+NSLocalizedFailureReasonErrorKey, or NSLocalizedFailureErrorKey. The steps this takes to construct the description include:
1. Look for NSLocalizedDescriptionKey in userInfo, use value as-is if present.
2. Look for NSLocalizedFailureErrorKey in userInfo. If present, use, combining with value for NSLocalizedFailureReasonErrorKey if available.
3. Fetch NSLocalizedDescriptionKey from userInfoValueProvider, use value as-is if present.
4. Fetch NSLocalizedFailureErrorKey from userInfoValueProvider. If present, use, combining with value for NSLocalizedFailureReasonErrorKey if available.
5. Look for NSLocalizedFailureReasonErrorKey in userInfo or from userInfoValueProvider; combine with generic "Operation failed" message.
6. Last resort localized but barely-presentable string manufactured from domain and code. The result is never nil.
*/
open var localizedDescription: String { get }
so, it is crashed (probably at step 6.) then this NSError is incorrectly constructed - so find who & how constructed it, possibly at some layer on underlying errors some key of userInfo unexpectedly is set as NSDictionary instead of NSError.

InvalidFirebaseData error when saving multiple values in Firebase

The following code once worked fine and I was able to save multiple values to a Firebase record. However this no longer works since upgrading to Swift 3 (Xcode 8). I now get the following error:
*** Terminating app due to uncaught exception 'InvalidFirebaseData', reason: '(setValue:) Cannot store object of type _SwiftValue at mood. Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.'
The above error always mentions the second value, regardless of what type it is (even if it is one of the supported types like NSString). Here's what I have:
postsRef.childByAutoId().setValue(["postedBy": self.currentUser?.uid, "mood": mood, "status": status, "date": convertedDate])
This still seems to comply with the docs on the Firebase website. What am I doing wrong?
Thanks!
Try :-
let toBePosted = ["postedBy": String(self.currentUser!.uid),
"mood": String(mood), "status": String(status),
"date": String(convertedDate)]
postsRef.childByAutoId().setValue(toBePosted){ (error, ref) -> Void in
}
As for converted date you will have to convert it to NSDate format when you retrieve it.
I just had this issue and after a few hours of toying around I think I've got a solution.
It looks like Firebase is wanting you to be very specific with your data types with Swift 3.
You need to straight up tell Swift what kind of dictionary you're going to have. In this case it's probably
let toBePosted : Dictionary<String, Any> = ["postedBy": String(self.currentUser!.uid),
"mood": String(mood), "status": String(status),
"date": String(convertedDate)]
"Any" is a new thing in Swift 3 I think. AnyObject doesn't work here because Swift redefined what AnyObjects are, and Strings/Ints dont seem to be them anymore.
Finally, it seems like you have to have absolutely no optionals in your dictionary. Maybe an optional is a _SwiftValue? Once I got rid of optionals and garunteed each value was going to be there it started working for me.
This worked for me, let me know if you're still having an issue.

iOS: syntax to retrieve value of attribute in array?

I have an array of user objects, one of which is stored in a property _selectedUser. When I log it out, it displays as :
{
uid = 5;
uname = xxxx;
}
However, when I try to access the uid with the following, I get an error:
NSNumber *useridnum = _selectedUser.uid;
Error:
[NSKnownKeysDictionary1 uid]: unrecognized selector sent to instance
What is the proper syntax to retrieve value for uid?
You are trying to access the key as a property on the dictionary, not as a value it contains.
As D.C. suggested in the comments, use this code:
NSNumber *useridnum = _selectedUser[#"uid];
Or:
NSNumber *useridnum = [_selectedUser objectForKey:#"uid"];
You said in the comments that the object is an NSManagedObject. I'm not familiar with Core Data, but it looks like you need to use valueForKey::
NSNumber *useridnum = [_selectedUser valueForKey:#"uid"];
An NSDictionary does not store the keys directly as properties; it stores them in some kind of private data structure. It wouldn't be possible to dynamically store keys as properties without messing with the runtime, and then only a very limited set of strings could be used as keys (instead of basically every possible object), because the keys would have to be C identifiers.
You get this error because when you tried to access the key as a property, the system sent a message to the object saying to call the getter method for the property, but that getter method didn't exist, so the app crashed.

Need help understanding a conditional crash when accessing my NSDictionary

I am keeping my data in a property called practiceRecords (an NSArray of dictionaries).
I check to see if the data already exists in the documents folder.
If yes, I load the data into self.practiceRecords.
If not, I build the array of dictionaries (using literal syntax), keeping this data in the self.practiceRecords property, and then write the data out to the documents folder.
(I am NOT reloading the data after writing it out)
As far as I am able to tell, there are no problems occurring during this process.
Then I have a step where I modify my data as follows ...
-(void)incNumberOfTriesFor:(NSString *)stringOfIndex {
if (self.practiceRecords)
{
int index = [stringOfIndex intValue];
int numberOfTries = [(NSNumber *)(self.practiceRecords[index][#"tries"]) intValue] + 1;
//CRASHING on this next line.
self.practiceRecords[index][#"tries"] = #(numberOfTries);
//message to helper method
[self writePracticeRecords];
}
}
So the first time through (when the array is built and written out) I get a crash at the indicated line.
The error is:
-[__NSDictionaryI setObject:forKeyedSubscript:]: unrecognized selector sent to instance
I quit the app, check the documents folder and see the data file written out with no issues.
I re-run the app, and then get no crash and the data file still looks great.
This is repeatable.
If the data file exists, no crash.
If the data first needs to be created, then a crash.
(In all cases, I manually look inside the resulting data file and see exactly what I expect to see - no issues there)
I'm not sure where to even begin squashing this bug, and would really like to understand the details of why this is happening.
Thanks very much for any help!
Just to recap the correct comments above:
-[__NSDictionaryI setObject:forKeyedSubscript:]: unrecognized selector sent to instance
NSDictionary does not implement any of the set... methods because it is immutable. You state that you're creating with literals syntax when the data is not found on disk. The literal syntax creates immutable containers
Instead, try...
// try to initialize from disk, but if not
// we can still use literal (immutable) syntax, but in a mutable container
self.practiceRecords = [NSMutableDictionary
dictionaryWithDictionary:#{ #"key" : #"value" }];

Saving custom class into Coredata

Hi i have used attribute type transformable and i have followed the procedure of archiving in one of the Forum .
But it says
CoreData: warning: no NSValueTransformer with class name 'SurveyResults' was found for attribute 'survey' on entity 'SurveyData'
2013-04-30 09:44:16.022 TestReachability[11641:c07] -[SurveyApplication encodeWithCoder:]: unrecognized selector sent to instance 0x845cb00
2013-04-30 09:44:16.023 TestReachability[11641:c07] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[SurveyApplication encodeWithCoder:]: unrecognized selector sent to instance 0x845cb00'
Here is my code
SurveyApplication *survey =[[SurveyApplication alloc]init];
survey.name=#"dheeraj";
survey.phone=#"573-356-2598";
NSManagedObject *aStory = [NSEntityDescription insertNewObjectForEntityForName:#"SurveyData"inManagedObjectContext:self.managedObjectContext];
[aStory setValue:survey forKey:#"survey"];
NSError *saveError = [[NSError alloc]init];
if (![self.managedObjectContext save:&saveError]) {
NSLog(#"%#",saveError);
}
SurveyAppplication object is my custom class and im trying to create an object and then store it core data .Could you please help me out.
Thanks
Dheeraj
It's not enough to make the attribute transformable, you also need to arrange for the transformation to happen. You can't just tell Core Data to transform any old object and expect it to know what to do. You have a couple of options:
Don't tell Core Data how to transform the data. In this case, Core Data will attempt to call encodeWithCoder: on your object to convert it to NSData. That's why you get the error that mentions this method-- it's trying to call the method on your class, but that method doesn't exist. In this case your class must conform to NSCoding for the transformation to occur.
Tell Core Data how to transform the data. In this case you create a subclass of NSValueTransformer that performs the transformation. You configure this on the attribute, either in the Core Data model editor or in code. In this case, you must have a custom transformer class that knows how to perform the transformation.

Resources