At the moment, I have a dictionary written like this, where Model is a generic:
private var cache = [String: Model]()
Sometime when I call the following:
cache.updateValue(model, forKey: uid)
I get the following crash:
Thread 14: "-[NSIndexPath count]: unrecognized selector sent to instance 0x8000000000000000"
Some other info about the example:
The dictionary is currently storing 1205 key/value pairs
The value being updated does not exist
According to the summary on Apples documentation for updateValue:
Updates the value stored in the dictionary for the given key, or adds a new key-value pair if the key does not exist.
Can someone please explain what is happening and how to fix this issue?
Related
I'm trying to pass information with an observed notification in my app.
The standard way to do that, is to set the userinfo. However, the data I want to pass is a Set, not a dictionary.
So, I do this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object:self.productIds)
The object arrives fine, but now I'm unable to get to it:
in the console I do this:
po notification.object!
▿ 2 elements
- 0 : ZTJ
- 1 : ZTM
However, when I try to get to one of the elements, I get this:
po notification.object![0]
error: <EXPR>:8:21: error: value of type 'Any' has no subscripts
notification.object![0]
What am I doing wrong?
You know that notification.object is a Set, but the compiler doesn't because it's declared as Any, which means it could be anything, and so it can't find which implementation of object[0] it should use.
To read this object you need to cast it to a set.
if let mySet = notification.object as? Set<MyType> {
// Do whatever
}
Keep in mind that the object property of Notification is designed to be used as a filter, If you pass a value when adding an observer, you'll only get notifications that are sent with that exact same object.
The userInfo dictionary is to send related information, like your set. In this case I would send a dictionary like this:
NotificationCenter.default.post(name: MY_NOTIFICATION_NAME, object: nil, userInfo: ["products": productIds])
The notification has an object type of Any?.
When you're poing it in the console, you're asking it to print its description, which Any can do.
When you're asking it to subscript, Any can't do that, because subscripting is not defined on that type. You need to cast it to the expected type:
po (notification.object as? [String])?[0]
In general, it's best to nail down the type of any Any as soon as you can. Think of Any as a box used to send things through the post. The first thing you do is open it and find out what's inside.
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
Admittedly, I have a hard time with the wording of the Apple Docs so I might be missing something. However, it doesn't seem to be the case.
Here's the question.
Given:
let charSomeUUID = CBUUID(string: "12345678-1234-12234-1234-123456789qwe")
someCharacteristic = CBMutableCharacteristic(type: charSomeUUID, properties: [CBCharacteristicProperties.read, CBCharacteristicProperties.notify], value: nil, permissions: CBAttributePermissions.readable)
How would one provide an initial value for the value field?
I see how to do it in didReceiveRead. But I'd like to set an initial value.
I've tried putting a string into a data object and putting it in the value spot and that doesn't seem to work. And the swift 3 equivalent of the (Objective C) example in the Apple docs doesn't seem to work (anymore?).
Thanks for the help.
Apologies.
I actually had another piece of code that was wrong that ended up being the real problem.
For future reverence here is code to show that what I originally thought would not work does, in fact, work.
let stringValue : String = "something"
let dataValue = stringValue.data(using: String.Encoding.utf8)
// Device Info
someCharacteristic = CBMutableCharacteristic(type: someUUID, properties: CBCharacteristicProperties.read, value: dataValue, permissions: CBAttributePermissions.readable)
NOTE: When adding a value by this method your characteristic needs to be read only. Still some things to read up on there.
Failing to use read only results in the following error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Characteristics with cached values must be read-only'
Sulthan, thank you for your attention. Sorry for the mix up.
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.
When debugging in XCode, the debugger is telling me that the NSDictionary object contains 1 key/value pair. When the debug console prints the description of the key/value pair is shows:
Printing description of testdictionary:
{
"Unknown (<1809>)" = <000000ff>;
}
I want to extract both the <1809> and the <000000ff>. I have tried both the valueForKey and objectforKey methods as described elsewhere on this site. But I think I am having difficulty understanding what is the key and what is the value here.
For example, is "Unknown (<1809>)" the key? Or is "<1809>" the key? Or is 1809 the key?
Thanks Tim for the reply.
The NSDictionary comes from the CoreBluetoothFramework the didDiscoverPeripheral: method is called and passes advertising data into an NSDictionary called "advertisementData".
This dictionary contains all sorts of stuff like the advertising channel and device name. However, I am trying to extract just the advertising data from "advertisementData". I used the key provided by corebluetooth "CBAdvertisementDataServiceDataKey" like this:
NSData* information;
information = [advertisementData objectForKey:CBAdvertisementDataServiceDataKey];
I was declaring "information" as an NSDictionary* object before. But changed it to NSData* after some more reading on Apples documentation. The result is the same. The debugger says that it contains a key/value pair as follows:
"Unknown (<1809>)" = <000000ff>;
Thanks again.
Nik
When you do not know the keys that are present in the dictionary, for example, because the key-value pairs come from an external source, you can use enumerateKeysAndObjectsUsingBlock: method to go through all key-value pairs present in the dictionary:
[testdictionary enumerateKeysAndObjectsUsingBlock::^(id key, id object, BOOL *stop) {
NSLog(#"The key is %#", key);
NSLog(#"The value is %#", object);
}];
I've never seen this before so this is nothing more than an educated guess:
The dictionary may have been casted from CFDictionaryRef, in which case both the key and value are const void * (instead of NSObject). The key might have been some Core Foundation type holding a file descriptor (hence 1809). The value could be a pointer (or an integer casted to a "pointer": (void *)32).
You should try and find out where the dictionary originates from, because it's the only thing that's going to give you any valuable information.
Update: the docs state that the value of CBAdvertisementDataServiceDataKey is a dictionary. The keys are CBUUID objects, representing CBService UUIDs and the values are NSData objects. (1)