-[__NSCFArray objectForKeyedSubscript:] error? - ios

I'm trying to get the data from a JSON response object in my iOS app after I log in. I keep getting this error though.
Error:
'NSInvalidArgumentException', reason: '-[__NSCFArray objectForKeyedSubscript:]: unrecognized selector sent to instance 0x8fc29b0'
Here is my code for the request, I'm using AFNetworking:
self.operation = [manager GET:urlString parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSDictionary *JSON = (NSDictionary *)responseObject;
NSDictionary *user = JSON[#"user"];
NSString *token = user[#"auth_token"];
NSString *userID = user[#"id"];
// NSString *avatarURL = user[#"avatar_url"];
// weakSelf.credentialStore.avatarURL = avatarURL;
weakSelf.credentialStore.authToken = token;
weakSelf.credentialStore.userId = userID;
weakSelf.credentialStore.username = self.usernameField.text;
weakSelf.credentialStore.password = self.passwordField.text;
[SVProgressHUD dismiss];
[self dismissViewControllerAnimated:YES completion:nil];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
if (operation.isCancelled) {
return;
}
[SVProgressHUD showErrorWithStatus:#"Login Failed"];
NSLog(#"%#", error);
}];
What the JSON response object looks like logged:
<__NSCFArray 0x8cac0b0>(
{
user = {
"auth_token" = b3a18e0fb278739649a23f0ae325fee1e29fe5d6;
email = "jack#jack.com";
id = 1;
username = jack;
};
}
)
I'm converting the array to a Dictionary using pointers like this:
EDIT: As pointed out in the comments incase anyone else stumbles across this with limited knowledge in iOS. I'm casting here not converting. See the answers for a full explanation.
NSDictionary *JSON = (NSDictionary *)responseObject;
I'm new to iOS, apologies if problem is obvious.
Thanks for any help.

The "conversion" you do is not doing any conversion, it's a cast. This simply tells the compiler to ignore the type it knows for this object and act as if it's the type you pass it.
Looking at the output you have, you don't get a dictionary back, but an array of dictionaries with a single dictionary. To get to the first dictionary you can use this instead of the cast:
NSDictionary *JSON = [responseObject objectAtIndex:0];
Note that since you get your data from a web service, you should probably also check if the contents you get are what you expect.

You say:
I'm converting the array to a Dictionary using pointers like this:
But that is not what you are doing. You are casting it, but the underlying object is still an array.
From the JSON response you can see that those JSON is constructed as an array with a single element which is a dictionary. You can get to the dictionary by calling [responseObject firstObject];. Of course, so that you don't get error going in the other direction, you should check how the input is constructed before calling any array or dictionary specific methods on the response object.

You have to convert your self but do not use casting.
Or, this is the code to detect if a json object is an array or dictionary
NSError *jsonError = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&jsonError];
if ([jsonObject isKindOfClass:[NSArray class]]) {
NSLog(#"its an array!");
NSArray *jsonArray = (NSArray *)jsonObject;
NSLog(#"jsonArray - %#",jsonArray);
}
else {
NSLog(#"its probably a dictionary");
NSDictionary *jsonDictionary = (NSDictionary *)jsonObject;
NSLog(#"jsonDictionary - %#",jsonDictionary);
}

Related

Obj-C: Check if object exists in NSMutableArray?

I'm trying to check if NSString 'testing' (47) exists inside of my NSMutableArray 'self.checkfriendData'. I'm using the code below, though after logging my if statement it appears as though it's never executed (even though the statement is true - see console data below, uid = 47, and thus hiding my object should fire?) Any idea as to why this isn't working? Help is much appreciated!
ViewController.m
NSMutableDictionary *viewParams3 = [NSMutableDictionary new];
[viewParams3 setValue:#"accepted_friends" forKey:#"view_name"];
[DIOSView viewGet:viewParams3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.checkfriendData = (NSMutableArray *)responseObject;
NSString *testing = #"47";
NSArray *friendorNo = self.checkfriendData;
if ([friendorNo containsObject:testing]) // YES
{
self.addFriend.hidden = YES;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
Here's what's inside self.checkfriendData:
2017-05-18 19:36:07.266529-0700 This is the friend data check (
{
body = "My name is Britt";
friendphoto = "/sites/default/files/stored/x.jpg";
"node_title" = "Britt";
uid = 47;
}
)
It appears that your NSArray contains NSDictionarys and you are asking if the array contains an NSString. The answer will always be no as the array doesn't directly contain any NSStrings.
If you want to search for the uid of 47 you will have to iterate over the array and check the uid key of each NSDictionary for the value 47.
The code for this would look something like:
for (NSDictionary *dict in friendorNo) {
if ([dict[#"uid"] isEqualToString:testing]) {
self.addFriend.hidden = YES;
}
}

Single object json parse - what am I doing wrong?

This has got to be something obvious that I am doing wrong. I have been banging my head against a wall trying to figure out what is going on. I already have this json parsing done in the android version of my app, now trying to parse this simple json in xcode and can't get it done.
NSError *myError = nil;
NSDictionary *res = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:&myError];
NSLog([res objectForKey:#"Date"]);
This code get me the "unrecognized selector sent to instance" error.
Here is the json data and you can see Date is one of the objects:
[{"Date":"2016-06-17T22:56:33.0811255-05:00"}]
Thanks in advance for any help on this issue. I've tried to simplify this post, but if more info is needed I will try and quickly provide.
http://i.stack.imgur.com/Y5fsT.png
JSONObjectWithData is returning an array of dictionaries and not a dictionary. Your print out of the raw JSON confirms this:
[{"Date":"2016-06-17T22:56:33.0811255-05:00"}] // This is an array
However you're attempting to treat that response object like a dictionary. In doing so you're calling a dictionary method (objectForKey:) on an array. This results in a crash. Try something like this:
NSError *error = nil;
id responseObject = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:&error];
if (error)
{
// Handle error
return;
}
NSLog(#"%#", responseObject);
if ([responseObject isKindOfClass:[NSArray class]])
{
NSArray *responseArray = (NSArray *)responseObject;
for (id item in responseArray)
{
NSLog(#"%#", item);
if ([item isKindOfClass:[NSDictionary class]])
{
NSDictionary *dictionary = (NSDictionary *)item;
NSString *dateString = [dictionary objectForKey:#"Date"];
NSLog(#"%#", dateString);
}
}
}
else
{
// responseObject is not an array...
}
I'm pretty sure this is because you should first set res as [NSDictionary]. Then pick the first element in that array and then get objectForKey: "Date". Normal JSON Data starts with a {, this starts with a [. Which means it's an array.
You can see it for yourself in the screenshot when it says #: 1 Element . THEN it says 1 key/value pair. The NSDictionary is inside an array.NSError
Try this code:
*myError = nil;
[NSDictionary] *res = [NSJSONSerialization JSONObjectWithData:self.responseData options:kNilOptions error:&myError];
if (res.count > 0) {
NSLog([res[0] objectForKey:#"Date"]);
}

Parsing JSON using objective - c

Im getting a JSON as below from a web service
data = {
following = 1;
};
message = "You are now following";
status = 1;
and I am trying to loop it using the following code (in order to get the value of the "following" key)
for(NSDictionary *item in datarecieved){
int placeName = [item valueForKey:#"following"];
NSLog(#"FOLLOWING VALUE %i", placeName);
}
But I am getting an exception - "uncaught exception of type NSException"
You have to do like this
for(NSDictionary *item in datarecieved){
if([item class] == [NSDictionary class])
{
int placeName = [item valueForKey:#"following"];
NSLog(#"FOLLOWING VALUE %i", placeName);
}
}
because there are two other key in response and they dosen't contain following key
Try it
int placeName = [datarecieved[#"data"][#"following"] intValue];
Once you deserialize the JSON it will be NSDictionary, so you don't need to use a loop (unless you need to loop through all keys)
In your case if you just want value for following key, you can do the following
NSNumber *following = [datarecieved valueForKeyPath:#"data.following"];
NSLog(#"FOLLOWING VALUE %#", following);
First of all, you need valid JSON. Look into the OP comments, there is a good address for understanding JSON.
Then you first must parse the JSON data. Assuming datareceived is of class NSData:
NSError *error;
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:datareceived options:nil error:&error];
if(error) {
NSLog(#"parsing error: %#", error);
}
else {
for(NSString *key in [dict allKeys]) {
NSLog(#"%# = %#", key, [dict objectForKey:key]);
}
}
The exception you are doubtless getting is unrecognized selector sent to instance 0x12345678 and is because item does not support that selector.
You need to check that item is actually an NSDictionary before calling valueForKey: (you should be using objectForKey: anyway):
for(NSDictionary *item in datarecieved){
if ([item isKindOfClass:[NSDictionary class]]) {
int placeName = [item objectForKey:#"following"];
NSLog(#"FOLLOWING VALUE %i", placeName);
}
}
(you probably want to break at some point once you've found what you're looking for as well).

In iOS, how do I parse a JSON string into an object

I come from Android dev, so sorry if I'm missing obvious iOS concepts here.
I have a JSON feed that looks like:
{"directory":[{"id":0,"fName":"...","lName":"...","title":"...","dept":"...","bld":"...","room":"...","email":"...","phone":"..."},{"id":1,"fName":"...","lName":"...","title":"...","dept":"...","bld":"...","room":"...","email":"...","phone":"..."}]}
Then, I have a Staff.h and .m with a class with properties to match it (id, fName, lName) ect.
I've been working at this for hours, but I can't seem to parse the JSON string to an array of Staff objects. The end goal is to get them into Core Data, so any advice would be nice.
Tutorials I've read haven't shown how to work with a JSON string in the form of {"directory":[{...}]} I had no problem doing this in my Android app, but I'm out of ideas here for iOS (6) in objective-c.
Thanks for reading.
You can do it like
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseObject options:0 error:&error];//response object is your response from server as NSData
if ([json isKindOfClass:[NSDictionary class]]){ //Added instrospection as suggested in comment.
NSArray *yourStaffDictionaryArray = json[#"directory"];
if ([yourStaffDictionaryArray isKindOfClass:[NSArray class]]){//Added instrospection as suggested in comment.
for (NSDictionary *dictionary in yourStaffDictionaryArray) {
Staff *staff = [[Staff alloc] init];
staff.id = [[dictionary objectForKey:#"id"] integerValue];
staff.fname = [dictionary objectForKey:#"fName"];
staff.lname = [dictionary objectForKey:#"lName"];
//Do this for all property
[yourArray addObject:staff];
}
}
}
Use: NSJSONSerialization
You use the NSJSONSerialization class to convert JSON to Foundation
objects and convert Foundation objects to JSON.
An object that may be converted to JSON must have the following properties:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
You will get NSDictionary then you can parse (create) it to your object and then use it in CoreData.
Use following code:
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
This will covert json data onto NSDictionary, which is similar to hashmap on android. I think this will help you. :)
you can use NSJSonSerialisation or AFNetworking library. Here is the example of AFNetworking to parse json response
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://example.com/resources.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
NSDictionary *json = (NSDictionary *)responseObject;
NSArray *staffArray = json[#"directory"];
[staffArray enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop){
Staff *staff = [[Staff alloc] init];
staff.id = [[obj objectForKey:#"id"] integerValue];
staff.fname = [obj objectForKey:#"fName"];
staff.lname = [obj objectForKey:#"lName"];
//add data to new array to store details
[detailsArray addObect:staff);
} ];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
then use Core Data framework to store data.
I would take a look at RestKit. It provides object-mapping and CoreData backed storage.
For this, you can SBJSON framework.
You have to convert the response string into an NSDictionary like
NSString *responseString = [[NSString alloc]initWithData:responseData encoding:NSUTF8StringEncoding];
NSDictionary *dic = [responseString JSONValue];
Now you can create an object for Staff class.
Staff *staff = [[Staff alloc]init];
Then you can store values in this object like
staff.firstname = [[[dic objectForKey:#"directory"] objectAtIndex:0] objectForKey:#"fName"];
Now you can pass this single object to other classes
You can use the handmade solution proposed by #janak-nirmal, or use a library like jastor, https://github.com/elado/jastor, it doesn't make much difference.
I warn you against Restkit, because the ratio benefits-vs-pain is very low, in my opinion.
Moreover, it could be as use a tank to kill a fly in your scenario.

NSDictionary objectForKey - need an NSArray with one element instead of just the element

I'm new to objective-c and need some help. In the code below, if the xml contains only one element, then the objectForKey calls will result in a single element instead of an NSArray with one element. How should I update the code to make sure that the line:
[[ApplicationManager sharedInstance] setTags:[[result objectForKey:XML_OPTION_LIST] objectForKey:XML_OPTION]];
always calls setTags with an NSArray (even if the xml contains zero or one element)?
NSString *XMLResponse = [request responseString];
NSError *parseError = nil;
NSDictionary *xmlDictionary = [XMLReader dictionaryForXMLString:XMLResponse error:&parseError];
NSDictionary *result = [xmlDictionary objectForKey:XML_RESULT];
if ([[[result objectForKey:XML_ERROR_CODE] objectForKey:XML_TEXT] isEqualToString:XML_ERROR_NONE]) {
[[ApplicationManager sharedInstance] setTags:[[result objectForKey:XML_OPTION_LIST] objectForKey:XML_OPTION]];
[[ApplicationManager sharedInstance] save];
}
The xml OPTION_LIST element contains one or more OPTION element. It's when there is only one OPTION element that the error occur (outside the scope of this code).
I appreciate your help.
The error occurs because you try to send objectForKey: message to some object that isn't an NSDictionary instance.
You would include a test to detect that and pass the setTags: method that element directly according to the test result.
if([[[result objectForKey:XML_ERROR_CODE] objectForKey:XML_TEXT] isEqualToString:XML_ERROR_NONE]) {
if([[result objectForKey:XML_OPTION_LIST] isKindOfClass:[NSDictionary class]]) {
[[ApplicationManager sharedInstance] setTags:[[result objectForKey:XML_OPTION_LIST] objectForKey:XML_OPTION]];
}
else {
[[ApplicationManager sharedInstance] setTags:[result objectForKey:XML_OPTION_LIST]];
}
[[ApplicationManager sharedInstance] save];
}

Resources