Well this is bizarre and I can't find any explanation.
The issue is that I am "transforming" a object property to a value into a NSDictionary and in a specific value it "encodes" a "special character" but on other values it doesn't.
This is the .h file for the object where I declare the properties:
#import <CoreLocation/CoreLocation.h>
#import "ModelObject.h"
#interface CheckinObject : ModelObject
#property (nonatomic,strong) NSString *name;
#property (nonatomic,strong) NSString *num;
#property (nonatomic,strong) NSString *date;
This is the .m file, where I synthesize and put it into a NSDictionary:
#implementation CheckinObject
#synthesize name;
#synthesize num;
#synthesize date;
//bunch of other uninteresting code
- (NSDictionary*)toDictionary {
NSLog(#"=> self.num : %#", self.num);
NSDictionary *dic = #{
#"date" :self.date,
#"name" :self.name,
#"num" :self.num
};
NSLog(#"=> dic.num : %#", dic[#"num"]);
NSLog(#"=> dic : %#", dic);
return dic;
}
This is the output:
2016-05-25 21:03:44.333 FoodTruckApp[1320:614394] => self.num : 23–187
2016-05-25 21:03:44.334 FoodTruckApp[1320:614394] => dic.num : 23–187
2016-05-25 21:03:44.335 FoodTruckApp[1320:614394] => dic : {
date = "2016-05-25 21:03:44";
name = "Alameda dos Carvalhos";
num = "23\U2013187";
}
Any thought on what I might be missing here?
Cheers.
Related
I am receiving below JSON response for saving user preferences in core data.
preferences={
1={
children=({
3=Samsung;4=Nokia;
});id=1;name=Mobiles;
};2={
children=({
5="Samsung Curve TV";
});id=2;name=Electronics;
};
};
Here is my code snippet which is working fine. But I think this is much verbose code.
NSLog(#"Preferences: %#", [response objectForKey:#"preferences"]);
for (NSDictionary *dic in [response objectForKey:#"preferences"]) {
NSLog(#"ID: %#", [[[response objectForKey:#"preferences"] objectForKey:dic] objectForKey:#"id"]);
NSLog(#"NAME: %#", [[[response objectForKey:#"preferences"] objectForKey:dic] objectForKey:#"name"]);
NSLog(#"Children DIC: %#", [[[[[response objectForKey:#"preferences"]
objectForKey:dic] objectForKey:#"children"] objectAtIndex:0] objectForKey:#"3"]);
for (NSDictionary *childDic in [[[[response objectForKey:#"preferences"]
objectForKey:dic] objectForKey:#"children"] objectAtIndex:0]) {
NSLog(#"Child Name: %#", [[[[[response objectForKey:#"preferences"]
objectForKey:dic] objectForKey:#"children"] objectAtIndex:0] objectForKey:childDic]);
}
}
I have 3 questions.
How can I improve my code snippet? is there any shorter way to implement this?
Is this JSON response is good enough for mobile end parsing? is it good JSON format? Is there any JSON response format we should follow as Mobile developers in terms of using Core data (which simply reduces DB implementation as best practice)?
How do I construct a JSON string like this again from Objective-c?
Ok so, (Sorry for NOT deep analysis) first of all I'd modify your JSON not to contain dictionary for dynamic elements (samsung has key 3 and so on, it should be array, make sense?)
I come up with imho better JSON structure:
{
"preferences":[
{
"items":[
{
"id" : "3",
"name" : "Samsung"
},
{
"id" : "3",
"name" : "Nokia"
}
],
"id":"1",
"name":"Mobiles"
},
{
"items":[
{
"id" : "3",
"name" : "Nokia"
}
],
"id":"2",
"name":"Electronics"
}
]
}
It's really easy now to map it to objects with JSONModel and only one thing you should care about is mapping keys.
NSString *JSONString = #" { \"preferences\":[ { \"items\":[ { \"id\" : \"3\", \"name\" : \"Samsung\" }, { \"id\" : \"3\", \"name\" : \"Nokia\" } ], \"id\":\"1\", \"name\":\"Mobiles\" }, { \"items\":[ { \"id\" : \"3\", \"name\" : \"Nokia\" } ], \"id\":\"2\", \"name\":\"Electronics\" } ] }";
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:[JSONString dataUsingEncoding:NSUTF8StringEncoding] options:0 error:nil];
NSError *mapError;
GlobalPreferences *globalPreferences = [[GlobalPreferences alloc] initWithDictionary:dictionary error:&mapError];
if (mapError) {
NSLog(#"Something went wrong with mapping model %#", mapError);
}
NSLog(#"Your mapped model: %#", globalPreferences);
Models:
Kinda GlobalPreference, root model, in case if u decide to add something extra
#import <JSONModel/JSONModel.h>
#import "Preference.h"
#interface GlobalPreferences : JSONModel
#property (nonatomic, strong) NSArray<Preference> *preferences; // using protocol here you specifying to which model data should be mapped
#property (nonatomic, strong) NSArray<Optional> *somethingElse; // some other settings may be here
#end
#import "GlobalPreferences.h"
#implementation GlobalPreferences
#end
Preference
#import <JSONModel/JSONModel.h>
#import "PreferenceItem.h"
#protocol Preference <NSObject>
#end
#interface Preference : JSONModel
#property (nonatomic, strong) NSNumber *ID; // required
#property (nonatomic, strong) NSString *name; // required
#property (nonatomic, strong) NSArray<PreferenceItem> *items;
#end
#import "Preference.h"
#implementation Preference
#pragma mark - JSONModel
+ (JSONKeyMapper *)keyMapper {
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"id": #"ID",
}];
}
#end
PreferenceItem
#import <JSONModel/JSONModel.h>
// This protocol just to let JSONModel know to which model needs to be parsed data in case if it's an array/dictionary
#protocol PreferenceItem <NSObject>
#end
#interface PreferenceItem : JSONModel
#property (nonatomic, strong) NSNumber *ID;
#property (nonatomic, strong) NSString *name;
#end
#import "PreferenceItem.h"
#implementation PreferenceItem
#pragma mark - JSONModel
+ (JSONKeyMapper *)keyMapper {
return [[JSONKeyMapper alloc] initWithDictionary:#{
#"id": #"ID",
}];
}
#end
Should be ok for coreData.
Maybe for you case all this is not necessary, but there are lot of things you need to care when u parsing/mapping network response such es data types, missing keys, error handling and so on. If u map it manually - you risk to have broken app some day.
I know you asked best practices for Objective-C, but I would suggest switching to Swift and using SwiftyJSON. With Swift the code is much more readable than with Objective-C.
let prefs = json["preferences"]
let userName = prefs["id"].stringValue
let name = prefs["name"].stringValue
let child = prefs["children"].arrayValue[0].arrayValue[3]
https://github.com/SwiftyJSON/SwiftyJSON
You can easily build your own JSON structures and send them with Alamofire:
let parameters: [String : AnyObject] = [
"preferences": [
"id": "123",
"name": "John",
"children": [
["3": "Three"]
]
]
]
let urlReq = NSMutableURLRequest(url)
urlReq.HTTPMethod = .POST
let req = Alamofire.ParameterEncoding.JSON.encode(urlReq, parameters: parameters).0
let request = Alamofire.request(req)
https://github.com/Alamofire/Alamofire
I have the following Mantle object & want to convert the dictionary to Mantle object. but the conversion always returns empty MTLModel object.
Any ideas?
//ABC.h
#interface ABC : MTLModel<MTLJSONSerializing>
#property (nonatomic, strong) NSString *Identifier;
#property (nonatomic, strong) NSString *Name;
#property (nonatomic, strong) NSString *ImageURLString;
#property (nonatomic, strong) NSString *Desc;
#property (nonatomic, strong) NSString *lastUpdated;
#end
// ABC.m
#implementation ABC
+ (NSDictionary *)JSONKeyPathsByPropertyKey {
return #{
#"Identifier" : #"id",
#"Name" : #"name",
#"Desc" : #"desc",
#"ImageURLString" : #"image_url",
#"lastUpdated" : #"last_updated_at"
};
}
#end
Calling code:
ABC *abc = [MTLJSONAdapter modelOfClass:ABC.class fromJSONDictionary:tempDict error:NULL];
The JSON dictionary is:
{
desc = "asdadasdakjqewqwmsdnasdaksdasd";
id = adsasdasdasdasd;
"image_url" = "http://upload.wikimedia.org/wikipedia/commons/thumb/1/19/abc.JPG";
"last_updated_at" = "2015-07-25 04:22:39.851710";
name = asdasdqwe;
}
The result ABC object has no contents. Can't figure what I am missing here.
Any ideas???
Try with this snippet and debug the adapterError. May have relevant information about the conversion from NSDictionary to Mantle object
//Create Mantle object from NSDictionary using MTLJSONSerialization
NSError *adapterError;
MTLModel *user = [MTLJSONAdapter modelOfClass:MTLModel.class fromJSONDictionary:dictionary error:&adapterError];
if(adapterError) {
PrintError(adapterError)
return nil;
}
id = adsasdasdasdasd;
needs to be
id = "adsasdasdasdasd";
I have a slightly complex nested JSON object that I need to map to Objective-C class. One think I need to do is skip levels in JSON when mapping. Here is what I mean:
I want to map the following JSON Dictionary:
NSDictionary *JSONDictionary = #{
#"status" : #"PASSWORD_EXPIRED",
#"_embedded" : #{
#"user" : #{
#"id" : #"00ub0oNGTSWTBKOLGLNR",
#"profile" : #{
#"login" : #"isaac#example.org",
#"firstName" : #"Isaac",
#"lastName" : #"Brock",
#"locale" : #"en_US",
#"timeZone" : #"America/Los_Angeles"
}
}
}
};
to
#interface Authentication : MTLModel <MTLJSONSerializing>
#property (strong, nonatomic) NSDate* expiresAt;
#property (strong, nonatomic) NSString* status;
#property (strong, nonatomic) NSString* relayState;
#property (strong, nonatomic) NSString* sessionToken;
#property (strong, nonatomic) NSString* stateToken;
//Embeded Resources
#property (strong, nonatomic) User* user;
#end
As you can see I am not creating an object for "_embeded". I am skipping it and mapping user to a User object.
Can this be achieved? If so How?
I have tried this in the implementation but it did not work:
#implementation Authentication
+ (NSDictionary*)JSONKeyPathsByPropertyKey {
return #{
#"expiresAt" : #"expiresAt",
#"status" : #"status",
#"sessionToken" : #"sessionToken",
#"stateToken" : #"stateToken",
#"relayState" : #"relayState",
#"user" : #"_embeded/user",
};
}
+ (NSValueTransformer *)userJSONTransformer {
return [NSValueTransformer mtl_JSONDictionaryTransformerWithModelClass:User.class];
}
#end
I used to do it with the following code:
+ (NSValueTransformer *)userJSONTransformer {
return [MTLValueTransformer reversibleTransformerWithForwardBlock:^id(NSDictionary *userDict) {
return [MTLJSONAdapter modelOfClass:[User class] fromJSONDictionary:userDict error:nil];
} reverseBlock:^id(User *user) {
return [MTLJSONAdapter JSONDictionaryFromModel:user];
}];
}
And also in +JSONKeyPathsByPropertyKey I think it should be #"user" : #"_embeded.user"
I have the json
{"Types":{
"food":[{"cve":"1","description":"Pizza"},{"cve":"2","description":"Restaurant"},{"cve":"3","description":"Cafe"}],
"Health":[{"cve":"3","description":"Pharmacy"},{"cve":"4","description":"Hospital"}]
} }
Types.h
#import <Foundation/Foundation.h>
#interface Types: NSObject
#property (nonatomic, copy) NSDictionary *types;
#end
Types.m
#import "Types.h"
#import <Motis/Motis.h>
#import "SubTipo.h"
#implementation Types
+ (NSDictionary*)mts_mapping
{
return #{#"types": mts_key(types),};
}
#end
Subtype.h
#import <Foundation/Foundation.h>
#interface Subtype: NSObject
#property (nonatomic, assign) int cve;
#property (nonatomic, copy) NSString *description;
#end
Subtype.m
#import "Subtype.h"
#import <Motis/Motis.h>
#implementation Subtype
+ (NSDictionary*)mts_mapping
{
return #{#"cve": mts_key(cve),
#"description": mts_key(description),
};
}
#end
I deserialize with
Types * values=[[Types alloc]init];
NSDictionary * jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
[values mts_setValuesForKeysWithDictionary:jsonObject ];
I get NSDictionary with NSArray of NSDictionary
but I need NSDictionary with NSArray of Subtypes
I try with
+ (NSDictionary*)mts_arrayClassMapping
{
return #{mts_key(types): Subtype.class};
}
but wasn't successful
How can I get these with Motis
As far as I see, your Types object is not properly defined. If you have an attribute of type NSDictionary* and the JSON received is a Dictionary, Motis won't perform any automatic conversion as the types already match (you are receiving a dictionary and your attribute is of type NSDictionary).
Therefore, you must implement your Type object following your JSON structure. This means that your Type object must have two properties of type array, one for food and one for health. Then, using the method +mts_arrayClassMapping you can specify the content type of the arrays to Subtype.
Here the implementation:
// ***** Type.h file ***** //
#interface Type: NSObject
#property (nonatomic, strong) NSArray *food;
#property (nonatomic, strong) NSArray *health;
#end
// ***** Type.m file ***** //
#implementation Type
+ (NSDictionary*)mts_mapping
{
return #{#"food": mts_key(food),
#"Health": mts_key(health),
};
}
+ (NSDictionary*)mts_arrayClassMapping
{
return #{mts_key(food): Subtype.class,
mts_key(health): Subtype.class,
};
}
#end
Regarding the implementation of Subtype, yours is already correct. However, you should not use the property name description as it is already being used by NSObject:
// ***** Subtype.h file ***** //
#interface Subtype: NSObject
#property (nonatomic, assign) NSInteger cve;
#property (nonatomic, copy) NSString *theDescription;
#end
// ***** Subtypes.m file ***** //
#implementation Subtype
+ (NSDictionary*)mts_mapping
{
return #{#"cve": mts_key(cve),
#"description": mts_key(theDescription),
};
}
#end
Finally, as you list above, you can map your JSON, but first you will have to extract the "dictionary" for key Types, which you will map to your "Type" model object.
// Get the json data
NSDictionary * jsonObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
// Extract the JSON dictionary of types.
NSDictionary *jsonType = [jsonObject objectForKey:#"Types"];
// Create a Type object
Type *type = [[Type alloc] init];
// Map JSON contents to the type object with Motis
[type mts_setValuesForKeysWithDictionary:jsonType];
Hoping this fixes your issue.
I have an array containing objects of a custom-made class. However, on initialization the compiler gives me an error - "Lexical or Preprocessor" Expected ':'
interface myClass : NSObject
#property (readwrite, strong) NSString* name;
#property (readwrite, strong) NSString* home;
#property (readwrite, strong) Preference pref; // This is another custom class
-(id) initWithName:(NSString*) name home:(NSString*) home preference:(Preference) preference;
end
#interface MyViewController()
#property (nonatomic, strong) NSArray *rowArray;
#end
#implementation MyViewController
...
...
...
- (void) initializeArray
{
self.rowArray = #{
[[myClass alloc] initWithName:#"Harry" home:#"New York" preference :Acura],
[[myClass alloc] initWithName:#"Win" home:#"Seattle" preference :Toyota];
};
}
Can someone tell me just where I am messing up and why I'm getting this error?
The Objective-C literal for an array is with square brackets,
NSArray *anArray = #[obj1, obj2].
In the code you posted it is trying to make a Dictionary,
NSDictionary *aDict = #{"key1" : obj1, #"key2" : obj2}
so this is why it is saying it expects a :.
The line should read,
self.rowArray = #[
[[myClass alloc] initWithName:#"Harry" home:"New York" preference :Acura],
[[myClass alloc] initWithName:#"Win" home:"Seattle" preference :Toyota];
];
As others have pointed out the there are a few other errors with the code and those city names are not NSString's, but I guess this is just an example snippet.