Showing posts with label Objective C. Show all posts
Showing posts with label Objective C. Show all posts

Saturday, January 02, 2016

Objective C and JSON, Convert a subclass of NSObject with properties that are other NSObjects to JSON

Check out GSObject on GitHub.

I have been investigating the serialization of Objective-C classes to JSON and have found that it is not as easy as I had hoped.

So, I wrote my own. Just like any good developer would do. ;-)

My solution is very simple and I was surprised at how simple it could be done compared to all of the solutions that I found out there.

The solution is based on these simple methods:


- (NSDictionary<NSString *,id> *)dictionaryWithValuesForKeys:(NSArray<NSString *> *)keys


+ (id)JSONObjectWithData:(NSData *)data options:(NSJSONReadingOptions)opt error:(NSError * _Nullable *)error


- (id)valueForKey:(NSString *)key

- (void)setValue:(id)value forKey:(NSString *)key

By overriding the valueForKey: and setValue:forKey in the new base class GSObject and then using some reflection/introspection and the dictionaryWithValuesForKeys: and JSONObjectWIthData:options:error I was able to write this simple and handy code.

Enjoy!


# GSObject

GSObject is a class that allows you to serialize to JSON or deserialize from JSON.

GSObject is a subclass of NSObject.
GSObject overrides valueForKey and setValue:forKey

GSObject also looks for the following SELECTORs:
- (NSString *) jsonValue
+ (NSNumber *) initWithJsonValue : (NSString *) jsonValue

These SELECTORS allow you to create subclasses or categories to control the JSON. Examples of usages would be with NSDate or NSNumber as currency.

Following are some simple classes to use as an example:


//
//  ThingOne.h
//  JasonStuff
//
//  Created by Geoffrey Slinker on 12/28/15.
//  Copyright © 2015 Slinkworks LLC. All rights reserved.
//

#import
#import "GSObject.h"
#import "ThingTwo.h"

@interface ThingOne : GSObject

@property (nonatomic, retain) NSString *name;
@property (nonatomic, retain) ThingTwo *thingTwo;
@property (nonatomic, retain) NSArray *values;
@property (nonatomic, retain) NSDictionary *dict;
@property int myInt;
@property float myFloat;
@property BOOL myBool;
@property (nonatomic, retain) NSNumber* someMoney;


@end

-----------------------------------------------------------------------

//
//  ThingOne.m
//  JasonStuff
//
//  Created by Geoffrey Slinker on 12/28/15.
//  Copyright © 2015 Slinkworks LLC. All rights reserved.
//

#import "ThingOne.h"

@implementation ThingOne

@synthesize name;
@synthesize thingTwo;
@synthesize values;
@synthesize dict;
@synthesize myInt;
@synthesize myFloat;
@synthesize myBool;
@synthesize someMoney;

- (instancetype)init
{
self = [super init];

thingTwo = [[ThingTwo alloc] init];

thingTwo.stuff = @"Thing Two Stuff";
thingTwo.someOtherStuff = @"Thing Two Other Stuff";
NSDateFormatter *dateFormater = [[NSDateFormatter alloc]init];
[dateFormater setDateFormat:@"yyyy-mm-dd"];
thingTwo.someDate =  [dateFormater dateFromString:@"1963-10-07"];

values = [NSArray arrayWithObjects:@"Value1", @"Value2", @"Value3", nil];

dict = [NSDictionary dictionaryWithObjectsAndKeys:@"value1", @"key1", @"value2", @"key2", nil];

myInt = 5431;
myFloat = 123.456f;
myBool = YES;

someMoney = [NSNumber numberWithInt:503];

return self;
}

@end

-----------------------------------------------------------------------

//
//  ThingTwo.h
//  JasonStuff
//
//  Created by Geoffrey Slinker on 12/28/15.
//  Copyright © 2015 Slinkworks LLC. All rights reserved.
//

#import
#import "GSObject.h"

@interface ThingTwo : GSObject

@property (nonatomic, retain) NSString *stuff;
@property (nonatomic, retain) NSString *someOtherStuff;
@property (nonatomic, retain) NSDate *someDate;
@property (nonatomic, retain) NSString *nullString;
@property (nonatomic, retain) NSDate *nullDate;

@end

-----------------------------------------------------------------------

//
//  ThingTwo.m
//  JasonStuff
//
//  Created by Geoffrey Slinker on 12/28/15.
//  Copyright © 2015 Slinkworks LLC. All rights reserved.
//

#import "ThingTwo.h"

@implementation ThingTwo

@synthesize stuff;
@synthesize someOtherStuff;
@synthesize someDate;

- (instancetype)init
{
self = [super init];

someDate = [NSDate date];

return self;
}

@end

-----------------------------------------------------------------------

Here is some example code on how to use it:

ThingOne* object1 = [[ThingOne alloc] init];
object1.name = @"John Jones";


NSData* jsonData1 = [object1 toJsonDataWithOptions:NSJSONWritingPrettyPrinted];
NSString *jsonString1 = [object1 toJsonStringWithOptions:NSJSONWritingPrettyPrinted];

NSDictionary *dict1 =  [GSObject dictionaryWithValues:object1];

NSString *roundTripJson1 = [object1 toJsonStringWithOptions:NSJSONWritingPrettyPrinted];

ThingOne *object2 = [GSObject create:[ThingOne class] fromJsonString:roundTripJson1];
NSString *roundTripJson2 = [object2 toJsonStringWithOptions:NSJSONWritingPrettyPrinted];


NSString *json3 = @"{\"myInt\" : 2039,\"thingTwo\" : {\"stuff\" : \"Thing Two Stuff2\",\"someOtherStuff\" : \"Thing Two Other Stuff2\"},\"name\" : \"William Smith\",\"dict\" : {\"key10\" : \"value10\",\"key20\" : \"value20\"},\"values\" : [\"ValueA\",\"ValueB\",\"ValueC\"]}";

ThingOne *object3 = [GSObject create:[ThingOne class] fromJsonString:json3];
NSDictionary *dict3 =  [GSObject dictionaryWithValues:object3];
NSString *roundTripJson3 = [object3 toJsonStringWithOptions:NSJSONWritingPrettyPrinted];

-----------------------------------------------------------------------
Here are the variables from the debugger:



(lldb) po jsonString1
{
  "values" : [
    "Value1",
    "Value2",
    "Value3"
  ],
  "myInt" : 5431,
  "myFloat" : 123.456,
  "myBool" : true,
  "someMoney" : "$503.00",
  "thingTwo" : {
    "stuff" : "Thing Two Stuff",
    "nullDate" : null,
    "someDate" : "1963-01-07 07:10:00 +0000",
    "nullString" : null,
    "someOtherStuff" : "Thing Two Other Stuff"
  },
  "name" : "John Jones",
  "dict" : {
    "key1" : "value1",
    "key2" : "value2"
  }
}

(lldb) po roundTripJson1
{
  "values" : [
    "Value1",
    "Value2",
    "Value3"
  ],
  "myInt" : 5431,
  "myFloat" : 123.456,
  "myBool" : true,
  "someMoney" : "$503.00",
  "thingTwo" : {
    "stuff" : "Thing Two Stuff",
    "nullDate" : null,
    "someDate" : "1963-01-07 07:10:00 +0000",
    "nullString" : null,
    "someOtherStuff" : "Thing Two Other Stuff"
  },
  "name" : "John Jones",
  "dict" : {
    "key1" : "value1",
    "key2" : "value2"
  }
}

(lldb) po roundTripJson2
{
  "values" : [
    "Value1",
    "Value2",
    "Value3"
  ],
  "myInt" : 5431,
  "myFloat" : 123.456,
  "myBool" : true,
  "someMoney" : "$503.00",
  "thingTwo" : {
    "stuff" : "Thing Two Stuff",
    "nullDate" : null,
    "someDate" : "1963-01-07 07:10:00 +0000",
    "nullString" : null,
    "someOtherStuff" : "Thing Two Other Stuff"
  },
  "name" : "John Jones",
  "dict" : {
    "key1" : "value1",
    "key2" : "value2"
  }
}

(lldb) po roundTripJson3
{
  "values" : [
    "ValueA",
    "ValueB",
    "ValueC"
  ],
  "myInt" : 2039,
  "myFloat" : 123.456,
  "myBool" : true,
  "someMoney" : "$503.00",
  "thingTwo" : {
    "stuff" : "Thing Two Stuff2",
    "nullDate" : null,
    "someDate" : "1963-01-07 07:10:00 +0000",
    "nullString" : null,
    "someOtherStuff" : "Thing Two Other Stuff2"
  },
  "name" : "William Smith",
  "dict" : {
    "key10" : "value10",
    "key20" : "value20"
  }
}

(lldb)


Monday, January 13, 2014

Objective C - Iterate over class properties and access property by class type for iOS

I needed a way to add an input accessory on some of my view controllers that have properties for many UITextView and UITextField properties. I didn't want to do it one at a time, I could miss one, or if I added or deleted one I would have to fix that manually as well.

So, I needed a way to walk the properties of my view controller, find all of the UITextView and UITextField properties, and call the method to set the input accessory view.

Here is the resulting code:


+ (NSArray *)allPropertyNamesFor: (id)target
{
    unsigned count;
    objc_property_t *properties = class_copyPropertyList([target class], &count);
    
    NSMutableArray *results= [NSMutableArray array];
    
    unsigned i;
    for (i = 0; i < count; i++)
    {
        objc_property_t property = properties[i];
        NSString *name = [NSString stringWithUTF8String:property_getName(property)];
        [results addObject:name];
    }
    
    free(properties);
    
    return results;
}



+ (void) attachToSubViewsOf: (NSObject *) target withAccessoryView: (UIView *)accesoryView {
    
    NSArray *properties = [self allPropertyNamesFor:target];
    
    for (NSString *propertyName in properties) {
        
        SEL sel =  NSSelectorFromString(propertyName);
        
        if ([target respondsToSelector:sel]) {
            
            id results = [target performSelector:sel withObject:nil];
            
            if( ([results isKindOfClass:[UITextView class]]) || ([results isKindOfClass:[UITextField class]]) ) {
                
                SEL setInputAccessoryViewSel = sel_registerName("setInputAccessoryView:");
                if ([results respondsToSelector:setInputAccessoryViewSel]) {
                    
                    [results performSelector:setInputAccessoryViewSel withObject:accesoryView];
                }
                
            }

        }
        
    }
    
}




Monday, April 19, 2010

UIView frame and bounds in GDB

I was trying to examine the frame property of a UILabel in the XCode debugger (GDB). Since I only do iPhone app development on occasion I tend to forget common tasks. I tried many different commands and none worked. I searched for posts on how to do this and found nothing specific.

So, here is how you do it:
(gdb) print (CGRect)[self.label frame]
$1 = {
origin = {
x = 20,
y = 20
},
size = {
width = 280,
height = 21
}
}

I have a View Controller that has an UILabel *label.

@interface TextViewController : UIViewController {
IBOutlet UILabel *label;
}

@property (nonatomic, retain) UILabel *label;

@end

Sunday, November 08, 2009

Objective C Method Signatures and Method Keywords

Objective C "looks" a bit different than the object oriented languages I have used previously. Object C and Pascal (Remember Lightspeed C and Pascal, single inheritance languages), C++, Java, and C#. I have not taken the time to learn Small Talk and so I do not know if Object C takes after Small talk or not.

I noticed that method signatures in Objective C have multiple "method keywords".

What is a method keyword? It is a word that proceeds the parameter type.

Here is a method from NSMutableDictionary:

-(void)setObject:(id)anObject forKey:(id)aKey

The method keywords are "setObject" and "forKey".

Because of many years developing in languages other than Objective C I find myself in the habit of decorating the method name. For example, if I have a method that inserts an object into some container and the object is stored by name then the method name would be like this:

void InsertObjectWithName (object insert, string name)

This habit shows up in my Objective C code.

@interface Foo : NSObject
{

}

-(void) insertValueWithName:(id) value : (NSString *)name;

@end

However, in Objective C you can have more than one method keyword.

@interface Foo : NSObject
{

}
-(void) insertValueWithName:(id)value :(NSString *)name;
-(void) insertValue:(id) value withName :(NSString *)name;
@end


(I intend the two methods to do exactly the same thing. Both are valid Objective C methods.)


This additional method uses two method keywords:

-(void) insertValue:(id) value withName :(NSString *)name;

At first I thought I liked either style the same. The more I use XCode the more I like the method with two method keywords because of how auto-completion reads.

Here is the single keyword style in XCode with auto-complete:



Here is the multiple keyword style in XCode with auto-complete:



For me I find that the method with two keywords reads clearer. Also I like the way the separation and highlighting of the auto-complete looks when multiple method keywords are used.

So, I am going to use multiple keywords in Objective C and avoid techniques that I use in other programming languages.

Slightly Off Topic

Syntax is just syntax and it doesn't take long to figure out the need to use brackets to invoke a method, or better said, to send a message to an object. For me it is still a bit painful to anticipate how many open brackets that I will need and I have to go back and forth in the source code inserting them as necessary.

It goes something like this:

Foo *myFoo = [Foo alloc]

Oh yeah, I better call the init...

Foo *myFoo = [Foo alloc] init]

Oh yea, I have to have another bracket at the front...

Foo *myFoo = [[Foo alloc] init]

And on and on I go back and forth inserting brackets where I need them.