Why is my JSONModel Class being treated as a NSDictionary? - objective-c

This is my JSON fetched from the API:
{
categories = (
{
icon = {
prefix = "https://ss3.4sqi.net/img/categories_v2/food/italian_";
suffix = ".png";
};
id = 4bf58dd8d48988d110941735;
name = "Italian Restaurant";
pluralName = "Italian Restaurants";
primary = 1;
shortName = Italian;
}
);
hasPerk = 0;
id = 4b5bed2ef964a520971d29e3;
location = {
address = "HS-5";
cc = IN;
city = "New Delhi";
country = India;
crossStreet = "Kailash Colony Market";
distance = 4061;
formattedAddress = (
"HS-5 (Kailash Colony Market)",
"New Delhi 110024",
Delhi,
India
);
labeledLatLngs = (
{
label = display;
lat = "28.55272788981442";
lng = "77.24192322690806";
}
);
lat = "28.55272788981442";
lng = "77.24192322690806";
postalCode = 110024;
state = Delhi;
};
name = "The Big Chill Cafe";
referralId = "v-1546605733";
}
And here is my model class:
#interface LocationModel : JSONModel
#property (nonatomic) NSInteger distance;
#property (nonatomic) NSInteger postalCode;
#property (nonatomic) NSString *address;
#property (nonatomic) NSString *cc;
#property (nonatomic) NSString *city;
#property (nonatomic) NSString *country;
#property (nonatomic) NSString *crossStreet;
#property (nonatomic) NSString *lat;
#property (nonatomic) NSString *lng;
#property (nonatomic) NSString *state;
#property (nonatomic) NSArray *formattedAddress;
#property (nonatomic) NSArray *labeledLatLngs;
#end
#protocol VenueModel;
#interface VenueModel : JSONModel
#property (nonatomic) NSArray *categories;
#property (nonatomic) BOOL hasPerk;
#property (nonatomic) NSString *id;
#property (nonatomic) LocationModel *location;
#property (nonatomic) NSString *name;
#property (nonatomic) NSString *referralId;
#end
#interface Restaurant : JSONModel
#property (nonatomic) NSInteger confident;
#property (nonatomic) NSArray <VenueModel*> *venues;
#end
Now, I am trying to get the values like this:
restaurant = [[Restaurant alloc] initWithDictionary
[NSJSONSerialization JSONObjectWithData:JSON
options:NSJSONReadingAllowFragments error:nil][#"response"] error:&error];
VenueModel *obj = [restaurant.venues firstObject];
LocationModel *locationObj = obj.location;
The values are coming fine till the VenueModel object, after that when I am trying to access the LocationModel object, i.e
LocationModel *locationObj = obj.location
, its throwing the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI location]: unrecognized selector sent to instance 0x600001b82880
Somebody please help me out, I need the location object.

NSJSONSerialization will produce NSDictionaries, NSArrays, NSStrings, NSNumbers and a few other basic types from a JSON string.
Your idea to initialize a custom object ([[Restaurant alloc] initWithDictionary...) is the right idea, but that idea must be applied to nested structures as well. So, for example, the Restaurant initializer must apply that idea to its venues array.
Otherwise, you'll get the crash you're getting, treating one of the venue objects as if its a VenueModel instead of what it really is (an NSDictionary).
You didn't post initWithDictionary, but it needs to look something like this, especially the part where the objects it contains are initialized...
- (id)initWithDictionary:(NSDictionary *)dictionary {
self = [super init];
// ...
self.venues = [NSMutableArray array];
if (self) {
for (NSDictionary *venueDictionary in dictionary[#"venues"]) {
VenueModel *venue = [[VenueModel alloc] initWithDictionary:venueDictionary];
[self.venues addObject:venue];
}
}
// and so on, for every other object type nested in dictionary
return self;
}
Moreover, as you can see, VenueModel must also follow this practice, turning the location data into a LocationModel, and so on...

Related

Objective C Acessing NSMutableDictionay inside NSMutableDictionary

I am a newcomer to objective C and I have serious problems in accessing NSMutableDictionarys.
I have two objects (Network and Beacon), and I want to create a NSMutableDictionary of Networks with a NSMutableDictionary of Beacons inside.
Network.h
#import <Foundation/Foundation.h>
#interface Network : NSObject{
NSString *id_network;
NSString *major;
NSString *active;
NSString *name;
NSString *status;
NSMutableDictionary *beaconsDictionary;
}
#property (nonatomic, strong) NSString *id_network;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *active;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSMutableDictionary *beaconsDictionary;
#end
Beacon.h
#import <Foundation/Foundation.h>
#interface Beacon : NSObject{
NSString *id_beacon;
NSString *major;
NSString *minor;
NSString *active;
NSString *detected;
}
#property (nonatomic, strong) NSString *id_beacon;
#property (nonatomic, strong) NSString *major;
#property (nonatomic, strong) NSString *minor;
#property (nonatomic, strong) NSString *active;
#property (nonatomic, strong) NSString *detected;
#end
I can create the NSMutableDictionary like this:
Beacon *beacon = [[Beacon alloc]init];
beacon.id_beacon=#"1";
beacon.major=#"1";
beacon.minor=#"1";
beacon.active=#"1";
beacon.detected=#"0";
NSMutableDictionary *beaconDic = [[NSMutableDictionary alloc]init];
[beaconDic setObject:beacon forKey:beacon.id_beacon];
Network *net = [[Network alloc]init];
net.id_network=#"1";
net.major=#"1";
net.active=#"1";
net.name=#"network 1";
net.status=#"1";
net.beaconsDictionary=beaconDic;
NSMutableDictionary *networkDic = [[NSMutableDictionary alloc]init];
[networkDic setObject:net forKey:net.id_network];
Ok, but now how can i access to beacon property "detected" directly and modify it?
I know this this is a very bad example, but I have no idea how to do it.
It looks like you'll have to have a network id and a beacon id to get where you need to go. It would looks something like:
Network *net = networkDic[netId];
Beacon *beacon = net.beaconsDictionary[beaconId];
beacon.detected = newDetectedValue;
This is for arbitrary network ids and beacon ids. You can hardcode values if you wish.
Edit:
It's worth noting in your example code that you can use the more modern dictionary assignment. Rather than [dictionary setValue:value forKey:key];, you can do dictionary[key] = value;. It's, of course, personal preference but you're very likely to see the latter in more recent things and I find it to be clearer.
You can get your Network and Beacon objects back by providing keys that match their keys in the dictionary:
NSString *nwKey = #"1";
Network *n = networkDic[nwKey];
NSDictionary *bDict = n.beaconsDictionary;
NSString *bnKey = #"1";
Beacon *b = bDict[bnKey];
Note: This is the new syntax. Here is the old one:
NSString *nwKey = #"1";
Network *n = [networkDic objectForKey:nwKey];
NSDictionary *bDict = n.beaconsDictionary;
NSString *bnKey = #"1";
Beacon *b = [bDict objectForKey:bnKey];

Working faster with variables that have almost the same name

Really strange question but I just can't find the right way to do this on the internet my self.
I have 3 NSStrings. called: string1, string2 and string3.
They all get value from the same UITextfield but at different times. So the values are different from each other.
What happens now is:
-(void)statement {
if (i==0) {
string1 = nameField.text;
} else if (i==1) {
string2 = nameField.text;
} else if (i==2) {
string3 = nameField.text;
}
}
Is it possible to replace the 1,2 and 3 of behind the 'string' with a variable or something so I can say something like:
-(void)statement {
stringX = nameField.text;
}
So that I can change X before the statement is activated?
Hope that it's all clear!
Thanks!
Declare a mutable array called, for example, myString
NSMutableArray *myString = [[NSMutableArray alloc] init];
then use something like
myString[i] = nameField.text
If those strings are properties on an object, you can use KVO:
#property (nonatomic, strong) NSString *string1;
#property (nonatomic, strong) NSString *string2;
#property (nonatomic, strong) NSString *string3;
Setter:
[object setValue:nameField.text forKey:[NSString withFormat:#"string%#", #(i)]];
Or create a selector and perform it:
// assuming
#property (nonatomic, strong) NSString *string1;
#property (nonatomic, strong) NSString *string2;
#property (nonatomic, strong) NSString *string3;
// then
-(void)statement {
NSString *selName = [NSString withFormat:#"setString%#", #(i)];
SEL sel = NSSelectorFromString(selName);
[self performSelector:sel withObject:nameField.text afterDelay:0];
}

Creating Dictionary from a model that contains nil values

I have the following Model:
#interface Person : NSObject
#property (nonatomic, copy) NSString *firstName;
#property (nonatomic, copy) NSString *middleName;
#property (nonatomic, copy) NSString *lastName;
#property (nonatomic, copy) NSString *status;
#property (nonatomic, copy) NSString *favoriteMeal;
#property (nonatomic, copy) NSString *favoriteDrink;
#property (nonatomic, copy) NSString *favoriteShow;
#property (nonatomic, copy) NSString *favoriteMovie;
#property (nonatomic, copy) NSString *favoriteSport;
-(NSDictionary *)getSomeInfo;
-(NSDictionary *)getAllInfo;
#end
Part 1:
I want getSomeInfo to return NSDictionary (e.g. {"firstName", self.firstName}) for all the fields that does not contain nil. How can I do that? (I could check every value but I wonder if there's a better way)
Part 2:
I want getAllInfo to return NSDictionary with all the property and if one contains nil then it should throw an error. Again do I have to write a long conditional statement to check or is there a better way?
Note: I want to do this without using external library. I'm new to the language so I'm open to suggestions if there's a better pattern in Objective-C.
There are two approaches.
1) Check each value:
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
if (self.firstName.length) {
res[#"firstName"] = self.firstName;
}
if (self.middleName.length) {
res[#"middleName"] = self.middleName;
}
// Repeat for all of the properties
return res;
}
2) Use KVC (Key-value coding):
- (NSDictionary *)getSomeInfo {
NSMutableDictionary *res = [NSMutableDictionary dictionary];
NSArray *properties = #[ #"firstName", #"middleName", #"lastName", ... ]; // list all of the properties
for (NSString *property in properties) {
NSString *value = [self valueForKey:property];
if (value.length) {
res[property] = value;
}
}
return res;
}
For the getAllInfo method you can do the same but instead return nil if any value is missing. Treat the nil results as your indication that not all properties have a value.

Can't use NSMutableArray data after Parsing with NSXMLParser (objective-c)

Hi I did parsing with NSXMLParser of some xml :
<company>
<name>Idan</name>
<country>Israel</country>
.....
<gender>man</gender>
</company>
I see that parsing success , now I have the MutableArray with one object that contain all strings (Idan,Israel etc.) but when I want to use this array, I can't get strings it contain.
When I do :
NSMutableArray *use = [pars users ];
NSLog(#"%#",use );
(users it's my array with object) I see:
<List:03f5a78>
where List in my code is:
#import <Foundation/Foundation.h>
#interface List : NSObject{
NSString *name;
NSString *country;
NSString *status;
NSString *gender;
}
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *country;
#property (nonatomic, strong) NSString *status;
#property (nonatomic, strong) NSString *gender;
#end
#import "List.h"
#implementation List
#synthesize name,date,city,country,status, gender;
#end
I try to do something like this:
NSMutableArray *use = [pars users.name ];
NSLog(#"%#",use );
but is not working, any ideas how to fix this?
Override the description method of your List class, and return a string which includes the values of all of the properties, then output it like you did the first time. The console will then print the value you returned.
Example:
#implementation List
...
...
-(NSString *)description
{
NSMutableString *desc = [NSMutableString string];
[desc appendFormat:#"name=%#, ", self.name];
[desc appendFormat:#"country=%#, ", self.country];
[desc appendFormat:#"status=%#, ", self.status];
[desc appendFormat:#"gender=%#", self.gender];
return desc
}
...
...
#end

Store scalar values (NSInteger, BOOL, double) in CoreData without converting to NSNumber

I have read a lot of articles where they say to explicitely convert from and to NSNumber when I want to store scalars in CoreData:
#property (nonatomic, assign) NSInteger value;
- (NSInteger)value
{
return [value integerValue];
}
- (void)setValue:(NSInteger)val
{
value = [NSNumber numberWithInteger:val];
}
But in our old project we have a bunch of properties where we doesn't do those manipulations (and they don't have custom accessors)! Why it works?
Example code
Declaration. Scalar values are not transient.
#interface ProductProperty : NSManagedObject
#property (nonatomic, strong, readonly) NSString * propertyID;
#property (nonatomic, strong, readonly) NSString * title;
#property (nonatomic, strong, readonly) NSSet * values;
#property (nonatomic, assign, readonly) BOOL filter;
#property (nonatomic, strong, readonly) NSDate *update;
#property (nonatomic, strong, readonly) NSNumber *index;
#property (nonatomic, assign, readonly) BOOL system;
#end
#import "ProductProperty.h"
#implementation ProductProperty
#dynamic propertyID;
#dynamic title;
#dynamic values;
#dynamic filter;
#dynamic update;
#dynamic index;
#dynamic system;
#end
Mapping into objects. Called if received JSON differs from existing. Otherwise it fetches from the CoreData storage.
- (void)updateProperties:(NSArray*)properties
{
for (NSDictionary *_property in properties) {
NSString *propertyID = [_property objectForKey:#"id"];
ProductProperty *property = [state.productPropertiesWithIDs objectForKey:propertyID];
if (!property) {
property = [state.ctx newObjectWithEntityName:ProductProperty.entityName];
property.propertyID = propertyID;
[state.productPropertiesWithIDs setObject:property forKey:propertyID];
}
property.update = state.update;
property.title = [_property objectForKey:#"title"];
property.filter = [_property objectForKey:#"filter"] ? [[_property objectForKey:#"filter"] boolValue] : YES;
property.index = [propertyIndexes objectForKey:propertyID] ? [propertyIndexes objectForKey:propertyID] : [NSNumber numberWithInt:propertyIndex++];
property.system = [SYSTEM_PROPERTY_IDS containsObject:propertyID] ? YES : NO;
[self updatePropertyValues:[_property objectForKey:#"values"] forProperty:property];
}
}
- (ProductProperty*)productPropertyWithID:(NSString*)propertyId error:(NSError**)error
{
NSFetchRequest *req = [ProductProperty request];
req.predicate = [NSPredicate predicateWithFormat:#"propertyID == %#", propertyId];
return [[ctx executeFetchRequest:req error:error] lastObject];
}
The answer is that since iOS 5 CoreData support auto generating accessors for scalars so I don't need to implement them manually.