JSONModel: Model Collection to JSON and Add Collection Manually - ios

Lets say I have this Model.
#protocol ProductModel
#end
#interface ProductModel : JSONModel
#property (assign, nonatomic) int id;
#property (strong, nonatomic) NSString* name;
#property (assign, nonatomic) float price;
#end
#implementation ProductModel
#end
#interface OrderModel : JSONModel
#property (assign, nonatomic) int order_id;
#property (assign, nonatomic) float total_price;
#property (strong, nonatomic) NSArray<ProductModel>* products;
#end
#implementation OrderModel
#end
Questions:
How I can add products in this array?
Then how I can produce the following JSON.
"products" : [
{
"id": "123",
"name": "Product #1",
"price": 12.95
},
{
"id": "137",
"name": "Product #2",
"price": 82.95
}
]
or the whole object.
{
"order_id": 104,
"total_price": 103.45,
"products" : [
{
"id": "123",
"name": "Product #1",
"price": 12.95
},
{
"id": "137",
"name": "Product #2",
"price": 82.95
}
]
}

I think BWJSONMatcher can help you out in a extremely neat way:
ProductModel *productModel1 = [[ProductModel alloc] init];
productModel1.id = 123;
productModel1.name = #"Product #1";
productModel1.price = 12.95;
ProductModel *productModel2 = [[ProductModel alloc] init];
productModel2.id = 137;
productModel2.name = #"Product #2";
productModel2.price = 82.95;
OrderModel *orderModel = [[OrderModel alloc] init];
orderModel.order_id = 104;
orderModel.total_price = 103.45;
orderModel.products = #[productModel1, productModel2];
NSString *producedJSON = [orderModel toJSONString];

Related

Parsing nested json in iOS using Mantle

I'm trying to parse data received from a service using the framework Mantle. The json has nested data and I am having problems to parse it. The json is like the following:
{
"sections": [
{
"title": "title1",
"level": 1,
"content": [
{
"type": "type1",
"text": "text1"
}
],
"images": []
},
{
"title": "title2",
"level": 2,
"content": [
{
"type": "type2",
"text": "text2"
},
{
"type": "type9",
"text": "text9"
},
{
"type": "type4",
"text": "text4"
},
{
"type": "type6",
"text": "text6"
}
],
"images": [
{
"src": "http://cvbcvcv",
"caption": "caption"
}
]
}]
}
The class that I am using is:
// MainObject.h
#interface MainObject : MTLModel <MTLJSONSerializing>
#property (strong, nonatomic) NSArray *sectionsArray;
+ (NSValueTransformer *)sectionsArrayJSONTransformer;
#end
#interface Section : MTLModel <MTLJSONSerializing>
#property (strong, nonatomic) NSString *title;
#property (assign, nonatomic) NSString *level;
#property (strong, nonatomic) NSArray *content;
#property (strong, nonatomic) NSArray *images;
+ (NSValueTransformer *)contentJSONTransformer;
+ (NSValueTransformer *)imagesJSONTransformer;
#end
#interface Content : MTLModel <MTLJSONSerializing>
#property (strong, nonatomic) NSString *type;
#property (strong, nonatomic) NSString *text;
#end
#interface Image : MTLModel <MTLJSONSerializing>
#property (strong, nonatomic) NSString *src;
#property (strong, nonatomic) NSString *caption;
#end
and
//MainObject.m
#implementation MainObject
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"sectionsArray" : #"sections",};
}
+ (NSValueTransformer *)sectionsArrayJSONTransformer
{
return [MTLJSONAdapter dictionaryTransformerWithModelClass:[Section class]];
}
#end
#implementation Section
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"title" : #"title",
#"level" : #"level",
#"content" : #"content",
#"images" : #"images",};
}
+ (NSValueTransformer *)contentJSONTransformer
{
return [MTLJSONAdapter arrayTransformerWithModelClass:[Content class]];
}
+ (NSValueTransformer *)imagesJSONTransformer
{
return [MTLJSONAdapter arrayTransformerWithModelClass:[Image class]];
}
#end
#implementation Content
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"type" : #"type",
#"text" : #"text",};
}
#end
#implementation Image
+ (NSDictionary *)JSONKeyPathsByPropertyKey
{
return #{
#"src" : #"src",
#"caption" : #"caption",};
}
#end
Then, when I make the call to the service and try to parse the json with the following code, being responseObject the data obtained from server, the data appears nil:
for (NSArray *array in [responseObject valueForKey:#"sections"]) {
NSArray *seccionArray = [MTLJSONAdapter modelsOfClass:[Section class] fromJSONArray:array error:nil];
}
I have tried a lot of ways to parse this data well, but the app always crashes or returns nil. I hope you can help me to solve this
Why can't just one line using NSJSONSerialization?
NSMutableDictionary *yourArray = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
Then you fetch what you want from your array...

How to parse the category object recursively in parsed JSON

Hi I am getting parsed nested JSON, which has many levels. I am able to get at first level value.
How could i use same model class to fetch all nested JSON values using recursion.
My JSON is -
{
"message": "Ok",
"STATUS_CODE": "200",
"REQUEST": {
"userid": "12436124",
"command": "GETCATEGORY"
},
"RESPONSE": {
"Category": [
{
"type": "Tag",
"categoryId": 11,
"name": "Electronics",
"catIconLeft": "",
"catIconRight": "",
"parentId": 0,
"Category": [
{
"type": "Category",
"categoryId": 84,
"name": "Mobile Accessories",
"parentId": 1,
"catIconLeft": "",
"catIconRight": "",
"Category": [
{
"type": "Product",
"categoryId": 90,
"name": "Headsets",
"catIconLeft": "",
"catIconRight": "",
"parentId": 9
},
<----so on------>
The complete Parsed JSON LINK
My Code for parsing-
-(void)call_CategoryListData{
[params setObject:#"command" forKey:#"GETCATEGORY"];
[params setObject:#"userid" forKey:#"12436124"];
[serverCall actionmethod:Fake_Category parameters:params onComplete:^(NSMutableDictionary* results){
if ([results isKindOfClass:[NSDictionary class]] || [results isKindOfClass:[NSMutableDictionary class]]){
//DDLogVerbose(#"\n\n\n\n\nResult----->%#",results);
NSMutableDictionary*responseDic=[results objectForKey:#"RESPONSE"];
NSMutableArray*catArray=[responseDic objectForKey:#"Category"];
for (NSMutableDictionary *dic in catArray) {
NSMutableArray* tmp = [dic objectForKey:#"Category"];
if (tmp) {
MyCategory *cat = [[MyCategory alloc] init];
cat.type = dic[#"type"];
cat.categoryId = dic[#"categoryId"];
if ([cat.type isEqualToString:#"Tag"]) {
cat.name = dic[#"name"];
cat.categoryId = dic[#"categoryId"];
[CatTag addObject:cat.name];
[CatID addObject:cat.categoryId];
<---------so on --------------->
NSLog(#"New Objects--->%#\n\n----->%#",CatTag,CatID);
}
}
}
}
}
onError:^(NSError *error) {
// handle error here
}];
}
My Model Class-
#interface MyCategory : NSObject
#property (nonatomic, strong) NSString *type;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *categoryId;
#property(nonatomic,strong) NSString *catIconLeft;
#property (nonatomic,strong) NSString *catIconRight;
#property (nonatomic,strong) NSString *parentId;
#property (nonatomic, strong) MyCategory*Category;
MyCategory.h file
#interface MyCategory : NSObject
#property (nonatomic, strong) NSString *type;
#property (nonatomic, strong) NSString *name;
#property (nonatomic, strong) NSString *categoryId;
#property (nonatomic, strong) NSString *catIconLeft;
#property (nonatomic, strong) NSString *catIconRight;
#property (nonatomic, strong) NSString *parentId;
#property (nonatomic, strong) NSArray *categories;
- (id)initWithRootDictionary:(NSDictionary *)dictionary;
#end
MyCategory.m hile
#implementation
- (id)initWithRootDictionary:(NSDictionary *)dictionary
{
self = [super init];
self.type = dictionary[#"type"];
self.name = dictionary[#"name"];
self.categoryId = dictionary[#"categoryId"];
self.catIconLeft = dictionary[#"catIconLeft"];
self.catIconRight = dictionary[#"catIconRight"];
self.parentId = dictionary[#"parentId"];
if (dictionary[#"category"]) {
NSMutableArray *categories = [NSMutableArray new];
for (NSDictionary *cat in dictionary[#"category"]) {
MyCategory *category = [[MyCategory alloc] initWithRootDictionary:cat];
[categories addObject:category];
}
self.categories = categories;
}
return self;
}
#end
//...
-(void)call_CategoryListData
{
//...
NSMutableDictionary * responseDic = [results objectForKey:#"RESPONSE"];
NSMutableArray * catArray = [responseDic objectForKey:#"Category"];
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *categoryDic in catArray) {
MyCategory *category = [[MyCategory alloc] initWithRootDictionary:categoryDic];
[result addObject:category];
}
// Do something with result
}
This is a fast written code directly in this editor without any IDE, so possible some syntax errors :)

Nested JSONModel in iOS to get info from Instagram

I'm working with JSONModel to parse Instagram info from JSON to a model in my iOS app. I have tried many ways to achieve it but I can't. Three main parts of the response are data, meta and pagination. I have created the model and the model is always NULL. I'm going to attach the response I'm trying to parse, my models and how I'm processing the response. I think it could be a problem with the structure but I don't know what I'm doing wrong. Thanks for your help.
Response from Instagram
{
"data": [
{
"attribution": null,
"caption": {
"created_time": "1387987595",
"from": {
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam"
},
"id": "618807262892627401",
"text": "Navidad :)"
},
"comments": {
"count": 2,
"data": [
{
"created_time": "1387987956",
"from": {
"full_name": "Luisa Bellydancer",
"id": "548519361",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_548519361_75sq_1398196543.jpg",
"username": "luisacantillo"
},
"id": "618810289837938356",
"text": "Te amo beb\u00e9"
},
{
"created_time": "1403666639",
"from": {
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam"
},
"id": "750332614214522476",
"text": "#sacanamboy"
}
]
},
"created_time": "1387987595",
"filter": "Walden",
"id": "618807262548694878_268167404",
"images": {
"low_resolution": {
"height": 306,
"url": "http://scontent-b.cdninstagram.com/hphotos-xpf1/t51.2885-15/1515365_559830227444878_1414305160_a.jpg",
"width": 306
},
"standard_resolution": {
"height": 640,
"url": "http://scontent-b.cdninstagram.com/hphotos-xpf1/t51.2885-15/1515365_559830227444878_1414305160_n.jpg",
"width": 640
},
"thumbnail": {
"height": 150,
"url": "http://scontent-b.cdninstagram.com/hphotos-xpf1/t51.2885-15/1515365_559830227444878_1414305160_s.jpg",
"width": 150
}
},
"likes": {
"count": 2,
"data": [
{
"full_name": "Luisa Bellydancer",
"id": "548519361",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_548519361_75sq_1398196543.jpg",
"username": "luisacantillo"
},
{
"full_name": "Steven Zambrano",
"id": "198842014",
"profile_picture": "http://photos-d.ak.instagram.com/hphotos-ak-xpa1/10354559_1419915091614315_879045002_a.jpg",
"username": "steven_zambrano"
}
]
},
"link": "http://instagram.com/p/iWce08pZde/",
"location": null,
"tags": [
"sacanamboy"
],
"type": "image",
"user": {
"bio": "",
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam",
"website": ""
},
"users_in_photo": [
{
"position": {
"x": 0.39722222,
"y": 0.15416667
},
"user": {
"full_name": "Luisa Bellydancer",
"id": "548519361",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_548519361_75sq_1398196543.jpg",
"username": "luisacantillo"
}
},
{
"position": {
"x": 0.55138886,
"y": 0.09583333
},
"user": {
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam"
}
}
]
},
{
"attribution": null,
"caption": {
"created_time": "1391910835",
"from": {
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam"
},
"id": "651717783438333290",
"text": "Vista de Bogot\u00e1"
},
"comments": {
"count": 1,
"data": [
{
"created_time": "1403665438",
"from": {
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam"
},
"id": "750322542532990878",
"text": "#sacanamboy"
}
]
},
"created_time": "1391910835",
"filter": "Hudson",
"id": "651717782742079012_268167404",
"images": {
"low_resolution": {
"height": 306,
"url": "http://scontent-b.cdninstagram.com/hphotos-xfp1/t51.2885-15/1889287_611340528913277_403885343_a.jpg",
"width": 306
},
"standard_resolution": {
"height": 640,
"url": "http://scontent-b.cdninstagram.com/hphotos-xfp1/t51.2885-15/1889287_611340528913277_403885343_n.jpg",
"width": 640
},
"thumbnail": {
"height": 150,
"url": "http://scontent-b.cdninstagram.com/hphotos-xfp1/t51.2885-15/1889287_611340528913277_403885343_s.jpg",
"width": 150
}
},
"likes": {
"count": 3,
"data": [
{
"full_name": "Luisa Bellydancer",
"id": "548519361",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_548519361_75sq_1398196543.jpg",
"username": "luisacantillo"
},
{
"full_name": "Stiven Sepulveda Casta\u00f1o",
"id": "270408040",
"profile_picture": "http://photos-a.ak.instagram.com/hphotos-ak-xfp1/10354393_1459814907595448_1770710968_a.jpg",
"username": "johntato"
},
{
"full_name": "Julian Garcia",
"id": "318551590",
"profile_picture": "http://photos-e.ak.instagram.com/hphotos-ak-xaf1/10413240_284995708339980_626057267_a.jpg",
"username": "juliangarcia20"
}
]
},
"link": "http://instagram.com/p/kLXd7WJZYk/",
"location": {
"id": 1187760,
"latitude": 4.67413252,
"longitude": -74.037993338,
"name": "Mirador de La Calera, Bogota"
},
"tags": [
"sacanamboy"
],
"type": "image",
"user": {
"bio": "",
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam",
"website": ""
},
"users_in_photo": [
{
"position": {
"x": 0.3625,
"y": 0.54444444
},
"user": {
"full_name": "Luisa Bellydancer",
"id": "548519361",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_548519361_75sq_1398196543.jpg",
"username": "luisacantillo"
}
},
{
"position": {
"x": 0.60694444,
"y": 0.42222223
},
"user": {
"full_name": "Camilo Sacanamboy",
"id": "268167404",
"profile_picture": "http://images.ak.instagram.com/profiles/profile_268167404_75sq_1386784112.jpg",
"username": "csacanam"
}
}
]
}
],
"meta": {
"code": 200
},
"pagination": {
"deprecation_warning": "next_max_id and min_id are deprecated for this endpoint; use min_tag_id and max_tag_id instead",
"min_tag_id": "1403666639684031",
"next_min_id": "1403666639684031"
}
}
Models
#interface TagPicturesInstagram : JSONModel
#property (strong,nonatomic) NSArray<PictureInstagram> *data;
#property (strong,nonatomic) MetaInstagram *meta;
#property (strong,nonatomic) PaginationInstagram *pagination;
#end
#protocol PictureInstagram
#end
#interface PictureInstagram : JSONModel
#property (strong,nonatomic) NSString<Optional> *attribution;
#property (strong,nonatomic) CaptionInstagram *caption;
#property (strong,nonatomic) CommentsInstagram *comments;
#property (strong,nonatomic) NSString *created_time;
#property (strong,nonatomic) NSString *filter;
#property (strong,nonatomic) NSString *id;
#property (strong,nonatomic) ImagesInstagram *images;
#property (strong,nonatomic) LikesInstagram *likes;
#property (strong,nonatomic) NSString *link;
#property (strong,nonatomic) LocationInstagram<Optional> *location;
#property (strong,nonatomic) NSArray<Optional> *tags;
#property (strong,nonatomic) NSString *type;
#property (strong,nonatomic) UserInstagram *user;
#property (strong,nonatomic) NSArray<UserInPhoto> *users_in_photo;
//#property (assign,nonatomic) BOOL user_has_liked;
//#property (strong,nonatomic) NSString<Optional> *website;
#end
#interface MetaInstagram : JSONModel
#property (assign,nonatomic) int code;
#property (strong,nonatomic) NSString<Optional> *error_type;
#property (strong,nonatomic) NSString<Optional> *error_message;
#end
#interface PaginationInstagram : JSONModel
#property (strong,nonatomic) NSString<Optional> *next_max_tag_id;
#property (strong,nonatomic) NSString<Optional> *deprecation_warning;
#property (strong,nonatomic) NSString<Optional> *next_max_id;
#property (strong,nonatomic) NSString<Optional> *next_min_id;
#property (strong,nonatomic) NSString<Optional> *min_tag_id;
#property (strong,nonatomic) NSString<Optional> *next_url;
#end
#interface CaptionInstagram : JSONModel
#property (strong,nonatomic) NSString *created_time;
#property (strong,nonatomic) FromInstagram *from;
#property (strong,nonatomic) NSString *id;
#property (strong,nonatomic) NSString<Optional> *text;
#end
#interface CommentsInstagram : JSONModel
#property (assign,nonatomic) int count;
#property (strong,nonatomic) NSArray<CommentInstagram> *data;
#end
#interface ImagesInstagram : JSONModel
#property (strong,nonatomic) ImgInstagram *low_resolution;
#property (strong,nonatomic) ImgInstagram *standard_resolution;
#property (strong,nonatomic) ImgInstagram *thumbnail;
#end
#interface LikesInstagram : JSONModel
#property (assign,nonatomic) int count;
#property (strong,nonatomic) NSArray<FromInstagram> *data;
#end
#interface LocationInstagram : JSONModel
#property (assign,nonatomic) int id;
#property (assign,nonatomic) double latitude;
#property (assign,nonatomic) double longitude;
#property (strong,nonatomic) NSString *name;
#end
#interface UserInstagram : JSONModel
#property (strong,nonatomic) NSString<Optional> *bio;
#property (strong,nonatomic) NSString *full_name;
#property (strong,nonatomic) NSString *id;
#property (strong,nonatomic) NSString *profile_picture;
#property (strong,nonatomic) NSString *username;
#property (strong,nonatomic) NSString<Optional> *website;
#end
#interface UserInPhoto : JSONModel
#property (strong,nonatomic) PositionTagInstagram *position;
#property (strong,nonatomic) FromInstagram *user;
#end
#protocol FromInstagram
#end
#interface FromInstagram : JSONModel
#property (strong,nonatomic) NSString *full_name;
#property (strong,nonatomic) NSString *id;
#property (strong,nonatomic) NSString *profile_picture;
#property (strong,nonatomic) NSString *username;
#end
#protocol CommentInstagram
#end
#interface CommentInstagram : JSONModel
#property (strong,nonatomic) NSString *created_time;
#property (strong,nonatomic) FromInstagram *from;
#property (strong,nonatomic) NSString *id;
#property (strong,nonatomic) NSString *text;
#end
#protocol ImgInstagram
#end
#interface ImgInstagram : JSONModel
#property (assign,nonatomic) double height;
#property (strong,nonatomic) NSString *url;
#property (assign,nonatomic) double width;
#end
#interface PositionTagInstagram : JSONModel
#property (assign,nonatomic) double x;
#property (assign,nonatomic) double y;
#end
Getting response with AFNetworking
-(void) loadInstagramInfo
{
//1. Create request
NSString *string = [NSString stringWithFormat:#"%#", BaseURLString];
NSURL *url = [NSURL URLWithString:string];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
//2. Do request with AFNetworking
AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request];
operation.responseSerializer = [AFJSONResponseSerializer serializer];
[operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
NSString *json = [responseObject description];
NSError *err = nil;
self.pictures = [[TagPicturesInstagram alloc]initWithString:json error:&err];
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
NSLog(#"Error cargando fotos");
/*
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error Retrieving Weather"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView show];*/
}];
[operation start];
}
I created one .txt file with your json response and then fetch data that you required.
NSMutableArray *arrayData = [[NSMutableArray alloc]init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [documentsDirectory stringByAppendingPathComponent:#"json.txt"];
// NSString *content = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:NULL];
NSData *data = [NSData dataWithContentsOfFile:filePath];
NSMutableDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
NSLog(#"json = %#",json);
for (NSDictionary* dic in [json objectForKey:#"data"]) {
[arrayData addObject:dic];
}
NSLog(#"array data = %#",arrayData);
NSLog(#"meta code = %#",[[json valueForKey:#"meta"] valueForKey:#"code"]);
NSLog(#"pagination deprecation_warning = %#",[[json valueForKey:#"pagination"] valueForKey:#"deprecation_warning"]);
NSLog(#"pagination min_tag_id = %#",[[json valueForKey:#"pagination"] valueForKey:#"min_tag_id"]);
NSLog(#"pagination next_min_id = %#",[[json valueForKey:#"pagination"] valueForKey:#"next_min_id"]);
follow above code and do changes in your model. Hope this will help you.
This is a response string.
meta code = 200
pagination deprecation_warning = next_max_id and min_id are deprecated for this endpoint; use min_tag_id and max_tag_id instead
pagination deprecation_warning = 1403666639684031
pagination deprecation_warning = 1403666639684031

Can't map nested RestKit object

I have a problem, can't map a nested object of json. The problem is with the 'capacities' key.
This is the error:
restkit.object_mapping:RKMappingOperation.m:830 Did not find mappable relationship value keyPath 'capacities'
I know that the problem is with the mapping itself, but just can't figure what:
RKObjectMapping *capacityMapping = [RKObjectMapping mappingForClass:[Capacity class]];
[capacityMapping addAttributeMappingsFromDictionary:#{ #"capacityText" : #"capacityText",
#"priceDescriptionText" : #"priceDescriptionText",
#"priceText" : #"priceText" }];
RKObjectMapping *colorsMapping = [RKObjectMapping mappingForClass:[Colors class]];
[colorsMapping addAttributeMappingsFromDictionary:#{ #"ID" : #"idNum",
#"Name" : #"name",
#"colorHex" : #"colorHex",
#"imageUrl" : #"imageURL" }];
RKObjectMapping *deviceDataMapping = [RKObjectMapping mappingForClass:[DeviceData class]];
[deviceDataMapping addAttributeMappingsFromDictionary:#{ #"device.ID" : #"idNum",
#"device.Name" : #"name",
#"device.additionalFeatures" : #"additionalFeatures",
#"device.deviceName" : #"deviceName",
#"device.mainFeatures" : #"mainFeatures",
#"device.supportPagesLinks" : #"supportPagesLinks",
#"device.whatsInTheKit" : #"whatsInTheKit" }];
[deviceDataMapping addRelationshipMappingWithSourceKeyPath:#"capacities" mapping:capacityMapping];
[capacityMapping addRelationshipMappingWithSourceKeyPath:#"colors" mapping:colorsMapping];
[deviceDataMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"responseError"
toKeyPath:#"responseError"
withMapping:errorMapping]];
[[RKObjectManager sharedManager] addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:deviceDataMapping
method:RKRequestMethodGET
pathPattern:#"devices/:boneID"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];
This is the DeviceData class:
#interface DeviceData : NSObject
#property (nonatomic) ResponseError *responseError;
#property (copy, nonatomic) NSString *idNum;
#property (copy, nonatomic) NSString *name;
#property (copy, nonatomic) NSString *additionalFeatures;
#property (copy, nonatomic) NSString *deviceName;
#property (copy, nonatomic) NSString *mainFeatures;
#property (copy, nonatomic) NSString *supportPagesLinks;
#property (copy, nonatomic) NSString *whatsInTheKit;
#property (nonatomic) NSArray *capacities;
#property (assign, nonatomic) NSInteger boneID;
#end
This is the Capacity class:
#interface Capacity : NSObject
#property (copy, nonatomic) NSString *capacityText;
#property (nonatomic) NSArray *colors;
#property (copy, nonatomic) NSString *priceDescriptionText;
#property (copy, nonatomic) NSString *priceText;
#end
This is the Colors class:
#interface Colors : NSObject
#property (copy, nonatomic) NSString *idNum;
#property (copy, nonatomic) NSString *name;
#property (copy, nonatomic) NSString *colorHex;
#property (copy, nonatomic) NSString *imageURL;
#end
This is the json:
{
"responseError": null,
"device": {
"ID": null,
"Name": null,
"additionalFeatures": "additional features text",
"capacities": [
{
"capacityText": "16GB",
"colors": [
{
"ID": null,
"Name": null,
"colorHex": "#a68f76",
"imageUrl": "iphone_5s_black.png"
},
{
"ID": null,
"Name": null,
"colorHex": "#a9a9a9",
"imageUrl": "iphone_5s_black.png"
},
{
"ID": null,
"Name": null,
"colorHex": "#616065",
"imageUrl": "iphone_5s_black.png"
}
],
"priceDescriptionText": “iPhone 5S",
"priceText": "750$"
},{
"capacityText": “32GB",
"colors": [
{
"ID": null,
"Name": null,
"colorHex": "#a68f76",
"imageUrl": "iphone_5s_black.png"
},
{
"ID": null,
"Name": null,
"colorHex": "#a9a9a9",
"imageUrl": “iphone_5s_black.png"
},
{
"ID": null,
"Name": null,
"colorHex": "#616065",
"imageUrl": “iphone_5s_black.png"
}
],
"priceDescriptionText": “iPhone 5S",
"priceText": "750$"
}
],
"deviceName": "iPhone 5s",
"mainFeatures": “some main features text",
"supportPagesLinks": [
{
"linkText": “restore",
"linkUrl": “restore.pdf"
}],
"whatsInTheKit": "what inside the kit text"
}
}
Because your response descriptor has a nil key path and you use device.xxx in all source key paths of the deviceMapping you need to have an explicit relationship mapping so you can specify the source and destination key paths:
RKRelationshipMapping *capacitiesMapping = [RKRelationshipMapping relationshipMappingFromKeyPath:#"device.capacities" toKeyPath:#"capacities" withMapping:capacityMapping];
[deviceDataMapping addPropertyMapping:capacitiesMapping];

How to use OCMock to mock a service

I am trying to write a unit test for creating a checklistItem, with properties defined below:
typedef NS_ENUM (NSUInteger, ChecklistStatus) { Pending, Completed };
#protocol IChecklistItem <NSObject>
#property (nonatomic, assign, readonly) NSInteger Id;
#property (nonatomic, copy, readonly) NSString *Description;
#property (nonatomic, assign, readonly)BOOL IsCompleted;
#property (nonatomic, assign, readwrite) ChecklistStatus Status;
#property (nonatomic, strong, readwrite) NSDate *CompletedDate;
#property (nonatomic, copy, readwrite) NSString *CompletedByUserId;
#property (nonatomic, assign, readonly) NSInteger RoleId;
#property (nonatomic, assign, readonly) NSInteger GroupId;
#property (nonatomic, strong, readonly) NSArray<IChecklistNote> *Notes;
Right now I have this setup in my setup method in my XCTest:
_service = [[ChecklistService alloc]initWithUrl:[NSURL URLWithString:kcChecklistUrl] credentialsManager:self.credentialsManager];
And this is the rest of my Unit Test:
- (void)testCreateChecklistItem {
XCTAssertNotNil(_service);
CCChecklistItem *checklistItem = [CCChecklistItem new];
CCChecklistItem *newChecklistItem = [CCChecklistItem new];
newChecklistItem.Id = 2;
newChecklistItem.Description = #"This is the Description";
newChecklistItem.RoleId = 2;
newChecklistItem.GroupId = 3;
newChecklistItem.Notes = nil;
newChecklistItem.Status = Completed;
XCTestExpectation *checklistItemExpectation = [self expectationWithDescription:#"checklistItem"];
id delegate = OCMProtocolMock(#protocol(ChecklistServiceDelegate));
id mock = [OCMockObject mockForProtocol:(#protocol(IChecklistService))];
[[[mock stub] andReturn:newChecklistItem] createChecklistItem:checklistItem delegate:delegate];
OCMExpect(([delegate didCompleteCreateChecklistItem:[OCMArg checkWithBlock:^BOOL(id obj) {
CCChecklistItem *items = obj;
XCTAssertNotNil(items);
double checklistId = items.Id;
XCTAssert(checklistId != 0);
[checklistItemExpectation fulfill];
}]]));
[_service createChecklistItem:checklistItem delegate:delegate];
[self waitForExpectationsWithTimeout:5 handler:^(NSError *error) {
if(error) {
}
OCMVerifyAll(delegate);
}];
}
However, i get the error failed: caught "NSUnknownKeyException", "[<__NSCFString 0x7faeaad6bd60> valueForUndefinedKey:]: this class is not key value coding-compliant for the key CompletedBy."
The createChecklistItem is defined below:
- (void)createChecklistItem:(id<IChecklistItem>)checklistItem delegate:(NSObject<ChecklistServiceDelegate> *)delegate
{
NSMutableArray *mockChecklistItem = [NSMutableArray new];
[TestObjectLoader loadObject:mockChecklistItem
fromJSONFile:#"ChecklistItem"
withMapping:[MappingProvider checklistCreateMapping]];
if (delegate != nil && [delegate respondsToSelector:#selector (didCompleteCreateChecklistItem:)]) {
[delegate didCompleteCreateChecklistItem:(NSObject<IChecklistItem> *)mockChecklistItem];
}
}
Where Checklist.json is
{
"Id": 13,
"Desc": "Checklist Description",
"IsCompleted": "false",
"TypeId": 1,
"RoleId": 1,
"Status": "C",
"CompletedDateTime": "2015-06-23T00:00:00+00:00",
"CompletedBy": "AC",
"Notes": [
{
"Note": "test",
"CreatedBy": "AC",
"CreatedDateTime": "2015-06-23T00:00:00+00:00"
}
]
}
Basically, I want to mock the service and when createChecklistItem is called, I simply just want to set a random Id on the object and pass it back instead of reading it through the ChecklistService from the Json file. IS there anyway to do this? I incorporated the mock in my test but I do not believe i am using it correctly...
You don't actually need Checklist.json, you should just be returning a value with a different Id.
So...
- (void)createChecklistItem:(id<IChecklistItem>)checklistItem delegate:(NSObject<ChecklistServiceDelegate> *)delegate
{
NSMutableArray *mockChecklistItem = [NSMutableArray new];
[TestObjectLoader loadObject:mockChecklistItem
fromJSONFile:#"ChecklistItem"
withMapping:[MappingProvider checklistCreateMapping]];
if (delegate != nil && [delegate respondsToSelector:#selector (didCompleteCreateChecklistItem:)]) {
[delegate didCompleteCreateChecklistItem:(NSObject<IChecklistItem> *)mockChecklistItem];
}
}
should simply just be
checklistItem.Id = 2; //Random number

Resources