Separate objects or strings in NSMutableArray? - ios

I'm new with JSon stuff, so bear with me. I'm deserializing a JSon from a URL, and everything is fine until I try to separate the objects within. The app crashes and I get an error that I don't understand. Maybe you can help me see what I'm missing.
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if ([data length]>0 && error == nil) {
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (jsonObject != nil && error == nil) {
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
NSDictionary *deserializedDictionary = [[NSDictionary alloc] init];
deserializedDictionary = jsonObject;
NSLog(#"Deserialized Dictionary = %#",deserializedDictionary);
/*
LOG: Deserialized Dictionary = { d = "[{\"unit\":\"P101\",\"price\":36.0000,\"stat\":\"process\",\"type\":\"P12\"},{\"unit\":\"P102\",\"price\":38.0000,\"stat\":\"process\",\"type\":\"P13\"},..}
*/
NSMutableArray *dicts = [[NSMutableArray alloc] init];
dicts = (NSMutableArray *)deserializedDictionary[#"d"];
NSLog(#"Print dicts: %#",dicts);
/*
LOG: Print dicts: [{"unit":"P101","price":36.0000,"stat":"process","type":"P12"},{"unit":"P102","price":38.0000,"stat":"process","type":"P13"},..]
*/
NSLog(#"%#",NSStringFromClass([dicts class]));
//LOG: __NSCFString
NSMutableDictionary *myDict = [[NSMutableDictionary alloc] init];
for (myDict in dicts)
{
NSLog(#"myDict objectForKey: id-> %# myDict objectForKey: result-> %#",[myDict objectForKey:#"unit"],[myDict objectForKey:#"result"]);
}
}
And then I get this error:
[__NSCFString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x7fe97b327790
2016-03-08 11:29:12.946 Poop[49680:5673839] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFString countByEnumeratingWithState:objects:count:]: unrecognized selector sent to instance 0x7fe97b327790'
Help, please?

From your code and log i can understand that actual issue is on your server side as from these lines
NSMutableArray *dicts = [[NSMutableArray alloc] init];
dicts = (NSMutableArray *)deserializedDictionary[#"d"];
NSLog(#"Print dicts: %#",dicts);
/*
LOG: Print dicts: [{"unit":"P101","price":36.0000,"stat":"process","type":"P12"},{"unit":"P102","price":38.0000,"stat":"process","type":"P13"},..]
*/
NSLog(#"%#",NSStringFromClass([dicts class]));
//LOG: __NSCFString
Log says your variable named dicts is of type NSCFString not NSMutableArray and NSString don't have keys and Enumeration can not run on NSString type of object.
Issue is with your API response, which is not returning correct JSON.
Solution is to change on your server side to return array or dictionary instead of string.

See the edited code:
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:^(NSURLResponse *response, NSData *data, NSError *error){
if ([data length]>0 && error == nil) {
id jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingAllowFragments error:&error];
if (jsonObject != nil && error == nil) {
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
NSDictionary *deserializedDictionary = [[NSDictionary alloc] init];
deserializedDictionary = jsonObject;
NSLog(#"Deserialized Dictionary = %#",deserializedDictionary);
/*
LOG: Deserialized Dictionary = { d = "[{\"unit\":\"P101\",\"price\":36.0000,\"stat\":\"process\",\"type\":\"P12\"},{\"unit\":\"P102\",\"price\":38.0000,\"stat\":\"process\",\"type\":\"P13\"},..}
*/
NSMutableArray *responseArray = [[NSMutableArray alloc] init];
responseArray = deserializedDictionary[#"d"];
NSLog(#"Print responseArray: %#",responseArray);
/*
LOG: Print dicts: [{"unit":"P101","price":36.0000,"stat":"process","type":"P12"},{"unit":"P102","price":38.0000,"stat":"process","type":"P13"},..]
*/
NSLog(#"%#",NSStringFromClass([responseArray class]));
//LOG: __NSCFString
//The correct way of fast enumeration.
for (NSMutableDictionary *myDict in dicts)
{
NSLog(#"myDict objectForKey: id-> %# myDict objectForKey: result-> %#",[myDict objectForKey:#"id"],[myDict objectForKey:#"result"]);
}
}
Also, you should always check the existence of the key in the dictionary for each time you fetch the value from the dictionary, for this you can add this method in your HelperClass,
//Check is key exist in the dictionary
+(BOOL)validateKeyValueForData:(id)dataValue {
if([dataValue isEqual:[NSNull null]] || dataValue == nil)
{
return NO;
}
if([dataValue isKindOfClass:[NSArray class]] || [dataValue isKindOfClass:[NSMutableArray class]])
{
if([dataValue isEqual:[NSNull null]] || dataValue == nil || [dataValue count] <= 0)
{
return NO;
}
}
else
if([dataValue isKindOfClass:[NSDictionary class]] || [dataValue isKindOfClass:[NSMutableDictionary class]])
{
if([dataValue isEqual:[NSNull null]] || dataValue == nil || [dataValue count] <= 0)
{
return NO;
}
}
else if ([dataValue isKindOfClass:[NSString class]] || [dataValue isKindOfClass:[NSMutableString class]])
{
if([dataValue isEqual:[NSNull null]] || dataValue == nil || [dataValue length] <= 0)
{
return NO;
}
}
return YES;
}
And use this as
for (NSMutableDictionary *myDict in dicts)
{
//This way you make sure that the value for the specified key is exist in the dictionary.
if ([HelperClass validateKeyValueForData:myDict[#"id"]] && [HelperClass validateKeyValueForData:myDict[#"result"]]) {
NSLog(#"myDict objectForKey: id-> %# myDict objectForKey: result-> %#",[myDict objectForKey:#"id"],[myDict objectForKey:#"result"]);
}
}

It looks like dicts object is somehow being instantiated as a string. Take a look at the raw NSData, before it is parsed into a JSON object:
NSString *rawResponse = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Raw data: %#", rawResponse);
If you set a general breakpoint to catch all exceptions, it might stop where you are trying to iterate through your dicts array. You can do this by:
Select the Breakpoints Navigator
Click the '+' button in the lower left
Select 'Add Exception Breakpoint...'
In the popup options menu, select the Break 'On Catch' option

Related

Is it possible to create a dispatch_async(dipatch_get_main_queue(), ^{}); with a completion go get when the dispatch block is finished?

I have a piece of code that execute a coredata update of the database, and I would like to know when that block is finished. Is there a way to get it knowing when the coredata has finished to update the tables?
Main function:
NSMutableArray* responseArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
dispatch_async(dispatch_get_main_queue(), ^{
[self parseAndAddLovAll:responseArray toArray:self.objects];
});
Function used in dispatch:
- (void)parseAndAddLovAll:(NSMutableArray*)responseArray toArray:(NSMutableArray*)destinationArray
{
NSError *error;
DB_ListOfValue_manage *elements_to_store = [[DB_ListOfValue_manage alloc] init];
NSManagedObjectContext * context = [elements_to_store managedObjectContext];
for (int index=0; index < [responseArray count]; index++)
{
NSDictionary * responseArray2 = [[NSDictionary alloc] initWithDictionary:responseArray[index]];
NSString * table_to_store = [[NSString alloc] initWithString:[responseArray2 objectForKey:#"table"]];
NSArray * lignes = [[NSArray alloc] initWithObjects:[responseArray2 objectForKey:#"lignes"], nil];
id value;
// Check if LOV table or contact table
if ((([#"Table_contact" compare:table_to_store])!=NSOrderedSame)&&
(([#"Table_event" compare:table_to_store])!=NSOrderedSame))
{
for (NSDictionary * item in lignes[0])
{
value = [item objectForKey:#"codeevent"];
if ([value isEqualToNumber:[NSNumber numberWithInt:EVENT_ID]])
{//FIXME: bug to check when SYNC
elements_to_store = (DB_ListOfValue_manage*)[NSEntityDescription insertNewObjectForEntityForName:table_to_store inManagedObjectContext:context];
elements_to_store.code_event = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"id"];
elements_to_store.id = [value isKindOfClass:[NSNull class]] ? #"" : value;
value = [item objectForKey:#"used"];
elements_to_store.used = [value isKindOfClass:[NSNull class]] ? #"" : value;
if (![context save:&error]) {
#ifdef DEBUG
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
#endif
}
else{
#ifdef DEBUG
NSLog(#"Data saved to DB, table %# %# %#", table_to_store, elements_to_store.label1, elements_to_store.label2);
#endif
}
}
}
}
}
}

Objective C: How to check object type without a lot of if statements

I am trying to set my text string to a URL. The following code works, but I feel like I can refactor it to make it look neater.
NSString *text = #“”;
id json = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
if ([json isKindOfClass:[NSDictionary class]]) {
id data = json[#“data”];
if ([data isKindOfClass:[NSDictionary class]]) {
id value = data[#"value"];
if ([value isKindOfClass:[NSArray class]]) {
id url = [value valueForKey:#"url"];
if ([url isKindOfClass:[NSString class]]) {
text = url;
}
}
}
}
So far it has the whole "mountain of doom" going on and I want to know how can I check if the object type is correct without using so many if statements. Any tips or suggestions are appreciated.
Edit: This is the lite version of my code, but the concept is the same.
In my opinion, there are 2 ways to make it look neater and ignore if-else-nesting-hell.
Using return.
NSString *text = #“”;
id json = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
if (![json isKindOfClass:[NSDictionary class]]) {
return;
}
id data = json[#“data”];
if (![data isKindOfClass:[NSDictionary class]]) {
return;
}
id value = data[#"value"];
if (![value isKindOfClass:[NSArray class]]) {
return;
}
id url = [value valueForKey:#"url"];
if (![url isKindOfClass:[NSString class]]) {
return;
}
text = url;
Create a generic method which checks kind of class and return a safe value
- (id)safeValueFromObject:(id)object forKey:(NSString *)key class:(Class)valueClass {
if (![object respondsToSelector:#selector(valueForKey:)]) {
return [[valueClass alloc] init];
}
id result = [object valueForKey:key];
return [result isKindOfClass:valueClass] ? result : [[valueClass alloc] init];
}
Use
NSString *text = #"";
id json = [NSJSONSerialization JSONObjectWithData:[data dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
id data = [self safeValueFromObject:json forKey:#"data" class:NSDictionary.class];
id value = [self safeValueFromObject:data forKey:#"value" class:NSArray.class];
id url = [self safeValueFromObject:value forKey:#"url" class:NSString.class];
text = url;

parsing json and getting exception, reason: '-[__NSCFArray objectForKey:]: unrecognized selector sent to instance 0x7b1c7630

I am trying to get a value for a particular key from a dictionary but i get a [__NSCFArray objectForKey:]: unrecognized selector sent to instance
- (void)listCaredMembersSuccessResponse:(NSDictionary *)response {
[self hideActivityView];
if ([[response valueForKey:#"status"] caseInsensitiveCompare:NSLocalizedString(#"SUCCESS", nil)] == NSOrderedSame) {
NSDictionary *mainDict = [response objectForKey:#"data"];
NSArray *detailsArray = [mainDict objectForKey:#"Text"];
[appDelegate.proxiesListArr addObjectsFromArray:[ParserManager parseListCaredMembers:detailsArray]];
} else {
[[ClassObjects sharedCenter] showCustomAlert:#"" Message:NSLocalizedString(#"PROXIES_FAILURERESPONSE", nil)];
}
This is my json response:
{"Status":"Success","data":[{"Alias":"1-0","ID":80,"Icon":"","Items":[],"Params":{},"Text”:”Text1”,”Type":"group","Width":"170"},{"Alias":"1-1","ID":8000102,"Icon":"","Items":[],"Params":{},"Text”:”Text2”,”Type":"group","Width":"170"}]}
The problem is you have a NSArray not an NSDictionary. The NSArray has a count of 1 and contains an NSDictionary.
this line is wrong NSArray *detailsArray = [mainDict objectForKey:#"Text"];
NSArray *wrapper= [[NSJSONSerialization JSONObjectWithData:webData options:0 error:nil]objectForKey:#"data"];
for (NSDictionary *temp in wrapper) {
NSString *text=[temp objectForKey:#"Text"]; //Text may be NSString type
// THE REST OF YOUR CODE
}
Update
if ([[response valueForKey:#"status"] caseInsensitiveCompare:NSLocalizedString(#"SUCCESS", nil)] == NSOrderedSame) {
NSArray *mainDict = [response objectForKey:#"data"];
for (NSDictionary *temp in mainDict) {
NSString *text=[temp objectForKey:#"Text"]; //Text may be NSString type
// THE REST OF YOUR CODE
}
}

iOS How to add an object in array at 0 index and show in tableview cell?

I have an issue that an array having three main objects and i want to add one object on each array's 0 index
Here is URL link
In three sections Homes Plots and Commercial and i want to add All Homes All Plots and All Commercial in each section and added up their results in each section, mean in each section at top All Homes, All Plots and All Commercial
- (void) loadFromDictionary:(NSDictionary *)theDictionary{
_parent_id = -1;
_type_id = [[theDictionary objectForKey:#"type_id"] intValue];
_title = [[NSString alloc] initWithString:[theDictionary objectForKey:#"title"]];
_title_alt1 = [[NSString alloc] initWithString:[theDictionary objectForKey:#"title_alt1"]];
_title_alt2 = [[NSString alloc] initWithString:[theDictionary objectForKey:#"title_alt2"]];
if([theDictionary objectForKey:#"parent_id"])
_parent_id = [[theDictionary objectForKey:#"parent_id"] intValue];
if([theDictionary objectForKey:#"child_list"])
_child_list = [[NSMutableArray alloc] initWithArray:[[theDictionary objectForKey:#"child_list"] componentsSeparatedByString:#","]];
}
+ (void)getTypesWith:(void (^)(NSArray *, NSError *))completionHandler
{
[ZNetworkManager postDataForBackGround:nil atURL:[ZMappingManager getRequestURLToGetPropertiesTypes] completionHandler:^(NSArray *array, NSError *error)
{
NSMutableArray *typesDictionariesArray =[NSMutableArray array];
NSMutableDictionary* details = [NSMutableDictionary dictionary];
if (!error)
{
NSDictionary *fetchedDictionary = (NSDictionary*) array;
if([fetchedDictionary isKindOfClass:[NSDictionary class]] == NO)
{
[details setValue:#"Fetched dictionary is null" forKey:#"desription"];
completionHandler(nil ,[NSError errorWithDomain:#"MyDomain" code:1 userInfo:details]);
}
else
{
if([[[fetchedDictionary objectForKey:#"meta"] objectForKey:#"status"] isEqualToString:#"200"]){
NSDictionary *data = [fetchedDictionary objectForKey:#"response"];
if([data isKindOfClass:[NSDictionary class]] == NO)
{
[details setValue:#"Fetched dictionary is null" forKey:#"desription"];
completionHandler(nil ,[NSError errorWithDomain:#"MyDomain" code:1 userInfo:details]);
}
else
{
NSArray *allTypes = [data objectForKey:#"type"];
if([allTypes count] == 0)
{
[details setValue:#"Fetched dictionary is null" forKey:#"desription"];
completionHandler(nil ,[NSError errorWithDomain:#"MyDomain" code:1 userInfo:details]);
}
else
{
NSMutableArray *searchTypes = [[NSMutableArray alloc] init];
for (NSDictionary *typeDic in allTypes)
{
[typesDictionariesArray addObject:typeDic];
ZZameenType *newType = [[ZZameenType alloc] init];
[newType loadFromDictionary:typeDic];
[searchTypes addObject:newType];
NSArray *arrayforChild = [typeDic objectForKey:#"childs"];
for(NSDictionary *typeChild in arrayforChild){
[typesDictionariesArray addObject:typeChild];
ZZameenType *newChild = [[ZZameenType alloc] init];
[newChild loadFromDictionary:typeChild];
[searchTypes addObject:newChild];
newChild = nil;
}
newType = nil;
}
NSSortDescriptor *typeID_sort = [NSSortDescriptor sortDescriptorWithKey:#"type_id" ascending:YES];
[searchTypes sortUsingDescriptors:[NSArray arrayWithObjects:typeID_sort,nil]];
[ZGlobals saveSearchTypes:typesDictionariesArray];
completionHandler(searchTypes ,nil);
searchTypes = nil;
details = nil;
}
}
}else{
}
}
}
}];
}
Not entirely sure what issue you're having. If you're just wanting to insert objects into an array and a specific index - you'd do something like this:
[searchTypes insertObject: addObject:newType atIndex:0];

Error in connectionDidFinishLoading that I can resolve

I have a simple JSON array that is returned from a zip code passed to a third party service.
http://api.geonames.org/findNearbyPostalCodes?postalcode=94115&country=US&radius=5&username=frequentz
I get an unknown error when trying to deserialize the results and I'm not sure what is going wrong.
Here is my connectionDidFinishLoading method, which fires as anticiapated but always fails...and I get the error in the last else if. Ideas?
-(void) connectionDidFinishLoading:(NSURLConnection *)connection {
NSLog(#"connectionDidFinishLoading...");
self.zipCodes = [NSMutableArray array];
NSError *error = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:receivedData options:NSJSONReadingAllowFragments error:&error];
if (jsonObject != nil && error == nil) {
NSLog(#"Successfully deserialized...");
if ([jsonObject isKindOfClass:[NSDictionary class]]) {
NSDictionary *deserializedDictionary = (NSDictionary *)jsonObject;
NSLog(#"Deserialized JSON Dictionary = %#", deserializedDictionary);
for (NSDictionary *item in jsonObject) {
NSString *city = [item objectForKey:#"adminName2"];
NSString *stateAbbreviation = [item objectForKey:#"adminCode1"];
NSString *postalCode = [item objectForKey:#"postalCode"];
NSString *distance = [item objectForKey:#"distance"];
NSString *country = [item objectForKey:#"country"];
NSString *stateName = [item objectForKey:#"stateName"];
ZipCodes *zipCode = [[ZipCodes alloc] initWithName:city stateAbbrev:stateAbbreviation postalCode:postalCode distanceFromGPSZip:distance country:country stateFullName:stateName];
[self.zipCodes addObject:zipCode];
}
}
else if ([jsonObject isKindOfClass:[NSArray class]]){
NSArray *deserializedArray = (NSArray *)jsonObject;
NSLog(#"Deserialized JSON Array = %#", deserializedArray);
}
else {
/* Some other object was returned. We don't know how to deal
with this situation as the deserializer returns only dictionaries or arrays */
}
}
else if (error != nil){
NSLog(#"An error happened while deserializing the JSON data.");
}
}
I think you're using the wrong service --it should be ...findNearbyPostalCodesJSON.... To use the JSON service as far as I can tell from their website. This is their example URL:
http://api.geonames.org/findNearbyPostalCodesJSON?postalcode=8775&country=CH&radius=10&username=demo

Resources