Showing posts with label XCode. Show all posts
Showing posts with label XCode. 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, 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

Monday, November 09, 2009

XCode Quick Keys or Key Short Cuts

When learning any new programming environment there will be lots of experimentation. Often being unfamiliar with the IDE becomes a frustration while trying to learn a new language like Objective C or learn new classes and libraries found in Cocoa.

Frustration with the IDE may cause you to give up. I know my impatience is the culprit!

So, here are three Quick Keys that were not obvious to me, and I was too impatient to drop down all of the menus or read the key stroke preferences to discover.

Switching between the source file and header file:
⌥⌘↑
Option Command Up Arrow (Opt Command UpArrow)

Commenting code "in" or "out":
⌘/
Command Forward Slash (Cmd /)

Move to the next argument in the auto-completion list:
^/
Control Forward Slash (Control /)



⌘ Command key
⌃ Control Key
⌥ Option Key (aka Alt Key)
⇧ Shift Key

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.

Tuesday, September 15, 2009

How do you rotate an NSButton, NSTextField, or NSView?

How do you rotate an NSButton, NSTextField, or NSView?

First, you can do it and it is fairly easy to do! Here is what it will look like!



There are two NSView methods available to do this.

In OS 10.0 you have:

- (void)setFrameRotation:(CGFloat)angle

And in OS 10.5 you have:

- (void)setFrameCenterRotation:(CGFloat)angle

For some the above information will be sufficient. For those desiring more information please continue to read this posting.

Create a new "Cocoa Application". From the File menu choose "New Project" and then select "Cocoa Application".
















Name and save the project whatever you want. I named my project "RotatedControl".

In XCode add to the "Classes" folder a "New File". Choose "Objective-C class".

I named my class "Controller"















Add your IBOutlets to your new class. Here is my code:

//
// Controller.h
// RotatedControl
//
// Created by Geoffrey_Slinker on 9/13/09.
// Copyright 2009 Area 51. All rights reserved.
//

#import


@interface Controller : NSObject {
IBOutlet NSTextField *textField;
IBOutlet NSButton *button;
}

@property (retain) NSTextField *textField;
@property (retain) NSButton *button;

- (void) awakeFromNib;

@end

//
// Controller.m
// RotatedControl
//
// Created by Geoffrey_Slinker on 9/13/09.
// Copyright 2009 Area 51. All rights reserved.
//

#import "Controller.h"


@implementation Controller

@synthesize textField;
@synthesize button;

- (void) awakeFromNib
{
//[textField setFrameRotation:45.0];
//[button setFrameRotation:30.0];

[textField setFrameCenterRotation:45.0];
[button setFrameCenterRotation:-45.0];


}

@end


Now open the NIB file. In XCode double click "MainMenu.xib" found in the folder "NIB Files". This should launch Interface Builder (IB).

Add an "Object Controller" and set its class to be the class type you created above. In my case I set it to "Controller". See "Where is the Classes Tab" for detailed information.

Still in IB add a Button and a Text Field to your Window. Just select them from the Library of Objects and drag them onto the Window.



Now "hook up" the IBOutlets of the Controller to the controls on the window.


You can see I have "wired" or "connected" the button outlet to the button on the window. The same was done for the text field.

Also wire the the "Referencing Outlets" delegate to the "Application". The "Application is above the "Controller" in the MainMenu.xib view.



If I haven't missed any steps then from XCode just build and run the application.



Monday, September 14, 2009

Where is the Classes Tab in Interface Builder? It has been removed.

Where is the Classes Tab in Interface Builder? It has been removed.

Interface Builder 3.1.2 does not have it. I do not know which version was the first to "not have it" but I know my version doesn't.

This can cause some confusion with tutorials, texts, books, etc., that refer to the Classes Tab.

Many examples refer you to a screen shot of IB's UI like this one:


Well, stop looking for it because it is gone.

Interface Builder 3 looks like this:


For instance, if you are creating a class that has some IBOutlet's then create that class in XCode, add the IBOutlet's that you desire. Launch Interface builder by doubling clicking your NIB file in XCode.

Select an Object from this panel:



Drag the "Object" to "Main***.xib" window, the window that has "File's Owner, First Responder, etc".

Make sure this new object is selected and from the Tools Menu open the Identity Inspector. There is a menu at the top labeled "Class". Click this and select the class that you created in XCode. The one I created I named "Controller", so I select "Controller" from the Class drop down menu.


I hope that helps clear up things for some.