0

I'm trying to create an app that pulls json from an api with a set of instructions on how the interface must look like. So basically the json will contain an array of NSDictionary and each NSDictionary will be an object that is displayed on the screen.

In the NSDictionary will be all the details of how the object will be displayed such as type of object, location of object and size of the object.

I have written code to accept an array of buttons to the screen.

for (int i = 0; i < self.jsonObjects.count; i++) {
    NSDictionary *jsonObject = [self.jsonObjects objectAtIndex:i];
    if ([[jsonObject objectForKey:@"object"] isEqualToString:@"UIButton"]) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];

        NSNumber *x = [jsonObject objectForKey:@"xlocation"];
        NSNumber *y = [jsonObject objectForKey:@"ylocation"];
        NSNumber *width = [jsonObject objectForKey:@"width"];
        NSNumber *height = [jsonObject objectForKey:@"height"];
        NSString *title = [jsonObject objectForKey:@"title"];
        [button setFrame:CGRectMake(x.intValue, y.intValue, width.intValue, height.intValue)];
        [button setTitle:title forState:UIControlStateNormal];
        [self.view addSubview:button];
    }
}

Now I can have tons of if statements for each object and get the program to do the same thing but I am trying to avoid it.

Basically what I am asking is what is the best way to implement this to minimise coding and increase readability of the code.

This is code I wrote to mimic the json output for the button.

NSDictionary *button = [[NSDictionary alloc] initWithObjectsAndKeys:@"UIButton", @"object",@"PressMe",@"title",@"10",@"xlocation",@"10",@"ylocation",@"100",@"width",@"100",@"height", nil];

NSDictionary *button2 = [[NSDictionary alloc] initWithObjectsAndKeys:@"UIButton", @"object",@"Dont Press",@"title",@"10",@"xlocation",@"210",@"ylocation",@"100",@"width",@"100",@"height", nil];

self.jsonObjects = [[NSArray alloc] initWithObjects:button,button2, nil];

Since I must still create the api the json output can be very flexible.

I was thinking of having an array of arrays. where each array in the array is an array of buttons or array of textfields. then I will only need about 20-30 arrays to cover the different object types and can run through the main array and then run through the array of each button or textfield.

for Eli Ganem

object  UIView *    0x07145c60
UIResponder UIResponder 
_layer  CALayer *   0x07145e80
_tapInfo    id  0x00000000
_gestureInfo    id  0x00000000
_gestureRecognizers NSMutableArray *    0x00000000
_subviewCache   NSArray *   0x075213e0
_charge float   0
_tag    NSInteger   0
_viewDelegate   UIViewController *  0x00000000
_backgroundColorSystemColorName NSString *  0x00000000
_viewFlags  <anonymous struct>
user1898829
  • 3,437
  • 6
  • 34
  • 62
  • 1
    Frankly, the best way to crack this nut is to start coding and throw away the first 3-4 attempts. You will eventually come to understand the issues and settle on a reasonably good approach. – Hot Licks Apr 09 '13 at 12:15

4 Answers4

0

I would suggest encapsulating the code:

    UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    NSNumber *x = [jsonObject objectForKey:@"xlocation"];
    NSNumber *y = [jsonObject objectForKey:@"ylocation"];
    NSNumber *width = [jsonObject objectForKey:@"width"];
    NSNumber *height = [jsonObject objectForKey:@"height"];
    NSString *title = [jsonObject objectForKey:@"title"];
    [button setFrame:CGRectMake(x.intValue, y.intValue, width.intValue, height.intValue)];
    [button setTitle:title forState:UIControlStateNormal];

in a factory method with the following signature:

- (UIButton*)butttonFromJSONDescription:(NSDictionary*)jsonObject;

then your loop would become:

if ([[jsonObject objectForKey:@"object"] isEqualToString:@"UIButton"]) {
    [self.view addSubview:[self buttonFromJSONDescription:jsonObject]];
} else if ([[jsonObject objectForKey:@"object"] isEqualToString:@"UITextField"]) {
    [self.view addSubview:[self textFieldFromJSONDescription:jsonObject]];
}

I understand that the class of objects your would like to manage have different properties you might want to set in the JSON. This seems to prevent further abstraction.

sergio
  • 68,819
  • 11
  • 102
  • 123
0

You shouldn't create arrays of arrays, or have 30 if conditions. You should create an object according to the value of "object" with reflection. Something like this:

id object = [[NSClassFromString(jsonObject[@"object"]) alloc] init];

If you only send subclasses of UIView as your "object", then you can write this instead:

UIView *object = (UIView*)[[NSClassFromString(jsonOnject[@"object"]) alloc] init];
NSNumber *x = [jsonObject objectForKey:@"xlocation"];
NSNumber *y = [jsonObject objectForKey:@"ylocation"];
NSNumber *width = [jsonObject objectForKey:@"width"];
NSNumber *height = [jsonObject objectForKey:@"height"];
[object setFrame:CGRectMake(x.intValue, y.intValue, width.intValue, height.intValue)];

Write this first, just to see how it works:

UIView *myView = (UIView*)[[NSClassFromString(@"UISwitch") alloc] init];
myView.frame = CGRectMake(0,0,100,40);
[self.view addSubview:myView];

In order to change the properties of the object, iterate over your dictionary and perform the necessary selector. Here's an example to illustrate:

NSDictionary *jsonObject = @{@"setBackgroundColor:":[UIColor redColor]};
for (NSString *key in jsonObject)
{
    if ([myView respondsToSelector:NSSelectorFromString(key)])
    {
        [myView performSelector:NSSelectorFromString(key) withObject:jsonObject[key]];
    }
}
Eli Ganem
  • 1,479
  • 1
  • 13
  • 26
  • So I did that including [self.view addSubview:object]; but its not adding my button. This is the details about the object that was created in my console. see my original post – user1898829 Apr 09 '13 at 13:49
  • UIButton is a special case, because you have to set its type to UIButtonTypeRoundedRect or any other type in order to see it. Try UISwirch first to see that it works. I'll edit my answer... – Eli Ganem Apr 10 '13 at 08:14
  • That worked. Any suggestion on how to include most objects. If the only problem is with button then I can always write separate code for the button. I just don't want to have to write for 10 different objects. – user1898829 Apr 10 '13 at 08:59
  • You should iterate over all the key-values in the dictionary, check with the method `respondsToSelector` if your object has this property, and if so then perform this selector. I'll edit my answer to show an example. – Eli Ganem Apr 10 '13 at 11:11
  • what is o? What type is it? – user1898829 Apr 10 '13 at 12:27
  • I'm sorry, "o" is actually the view you created. I changed the names to make it more readable. The last snippet of code goes together with the before-last snippet – Eli Ganem Apr 10 '13 at 12:34
0

ive just implemented something similar to what you are doing in an app ive been working on. what i did was have json statements with a type, then an array of items. call these objects SectionItems so you will have a whole json array of SectionItems which you would read in from the json file into an NSArray. Then what i did was for each type that could exist for the SectionItems you would create a custom UITableViewCell. Create a UITableDataSource that will check the indexPath.row against the array of SectionItems that you read in from your json file, which will construct the appropriate UITableViewCell using the items in the SectionItems to display at that row.

so just for clarity this is what my SectionItems looks like:

@interface SectionItem : NSObject

@property (strong) NSString *type; //controls what type of cell will be made in the datasource
@property (strong) NSString *data; //auxiliary data
@property (strong) NSArray  *items; //actual data to populate the cell with

-(id) initWithJSON:(NSDictionary *)jsonData;

@end

hope this helps. i found this way useful, cause then if the data in the json is changed, everything just works. the way the json is formed completely controls how the data will be viewed.

Fonix
  • 11,447
  • 3
  • 45
  • 74
0

You could always use a framework to do the fetching/parsing, see this post.

Also, you could use the objective-c run-time if you'd prefer a more bespoke approach. I wrote a dynamic mapper for a recent project.

For example, you could sub-class UIButton, have an initWithDictionary: method that parses the json collection per button - and because you say you have great control over the web service, you could also combine the size and origin fields into a string representing the rect "{x,y,width,height}", and inside your initWithDictionary field, for the given 'frame' key, convert the string to a rect with CGRectFromString()

Community
  • 1
  • 1
user352891
  • 1,181
  • 1
  • 8
  • 14