0

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.

  1. How can I improve my code snippet? is there any shorter way to implement this?

  2. 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)?

  3. How do I construct a JSON string like this again from Objective-c?

swiftBoy
  • 35,607
  • 26
  • 136
  • 135
happycoder
  • 927
  • 3
  • 13
  • 28
  • learn how other people do, third party e.g https://github.com/icanzilb/JSONModel – Injectios Mar 25 '16 at 11:47
  • You have answer in your question. – user3182143 Mar 25 '16 at 13:15
  • @Injectios what do you suggest, can I convert above JSON response into JSONModel? I'm aware of JSONModel but I have no idea how this response is compatible with this? – happycoder Mar 25 '16 at 13:50
  • @user3182143 I'm sorry I didn't get you? Can you suggest proper and reliable sources that shows standard JSON format since I believe this is not good JSON response format. – happycoder Mar 25 '16 at 13:52
  • yes you can convert any kind of responses and nested elements it with JSONModel, only one thing you need to care is mapping keys (i'll try to write example) – Injectios Mar 25 '16 at 14:25

2 Answers2

0

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.

Injectios
  • 2,777
  • 1
  • 30
  • 50
  • Thank you for your detailed answer. What you mean is without having "items":[ { "id" : "3", "name" : "Nokia" } ] , this kind of keys (ex: id) on response its difficult to parse? My backend guys thinks they are experts and don't like to change JSON format. Can I still use their response with JSON Model? How do I explain this to them? :D (i.e. why asked any reliable sources to show what really other experts being doing) – happycoder Mar 26 '16 at 04:30
  • I guess, simply saying values from database shoudn't be a keys of a collection (dictionary etc), otherwise how u map it to you model? You need to create properties to parse json by those keys, right? You don't know the values, instead you know the fields in database (id, name so on) – Injectios Mar 28 '16 at 14:56
  • If you use their JSON, you can't have your custom model, you will just have to use NSDictionary as a model object that easy, pretty much the same like you already did. Or u need to map it manually like model.id = dictionary.key and so on. I guess this is not proper way of doing API, check f.e this out http://stackoverflow.com/questions/12806386/standard-json-api-response-format – Injectios Mar 28 '16 at 15:03
-1

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

Tapani
  • 3,191
  • 1
  • 25
  • 41