Thursday, 5 November 2009

Java Developer Diaries; Learning iPhone Development with Objective-C

Few days ago I have seen a tweed from an iPhone developer who I met during a Google meeting in San Francisco. I am quite used to hear comments from Java developers about how complicated Objective-C is, however, this time it was the opposite. She was just out of a Java session and was complaining about Java for too many import statements, not providing built in patterns for ease of development.
This reminded me Episode IV, A New Hope, where Han Salo was making fun of lightsabers and Obi-Wan saying;
This is the weapon of a Jedi Knight. Not as clumsy or random as a blaster. An elegant weapon for a more civilized time.

Don't get me wrong I am not saying Objective-C is elegant where Java is clumsy (actually still I am much more comfortable in Java). I like both Objective-C and Java just like I like both Obi-Wan and Han Solo, but the thing is they are different than each other.

You can't change Java or Objective-C to act like the other. They have different approaches, traditions and ways to do things. Objective-C's roots go back to 1986 which is much older than Java. Objective-C is much tidy and clean when compared too languages of its time.

As Apple built their OS and systems on it, they also provide built in patterns for ease and tidyness of coding. Development for iPhone is not an exception, unlike most other mobile platforms Objective-C forces you to use some built in patterns (such as MVC) even you if you do not realize.

I don't really like hello world tutorials so this post will be a jump start in to iPhone development with Objective-C but I will still try to explain things in Java developers perspective...

Our mission (we should choose to accept) is to built an iPhone app making use of accelarator, GPS and the orientation of the device built on native iPhone UI components. We can build an application displaying our latitude and longitude when the device has shaken and the UI will rotate as we rotate out phone. This application will make use of most of the interesting built in APIs and still will be simple enough for a beginner (hopefully).

To develop iPhone apps you will definitely need the iPhone SDK and the built in IDE provided by Apple, the XCode. I am sure you will find it difficult to adopt to xcode but as you get use to it you are going to like it. Start XCode and click new project.

Picture 29

A new window will popup with several project templates. XCode contains pre defined templates. Choose iPhone from the list on the left and scroll until you see View-based Application on the right pane.

Picture 1This template gives you a single view built on MVC pattern. Click choose and name your project as MyTutorial. As you finish, XCode will create a bunch files. Your project should look like the following;

Picture 2

To summarize quickly, .h files are interfaces and .m files are implemantation classes. The files with .xib subfix are UI objects which are not text based code files. They are real visual design objects and you can tweak them with the Interface Builder tool which comes bundled with XCode. main.m is your manin class where your application is fired up, appDelegate classes are the delegation classes where your view (xib file) and your Controller is put in to action in harmony. You do not need to change the those so lets quicly move to contoller classes where the actual magic occurs.

Objective-C really makes use of interfaces so whatever you are going to built you should start with controller.h file. Click MyTutorialViewController.h once and the lower pane will display the codes. You will notice the interface and the UIKit imports are already defined. Unlike Java, in Objective-C imports are handles as frameworks which is a simpler way to control dependencies. Objective-C also makes heavy use of Delegates (which you should have noticed one already created so far). To add functionality from most of the APIs, you need to add the approciate Delegate, sadly there is no silverbullet here but a bit googleing, some guess and some code assist from IDE should work most of the time. Since we want the functionality from the acceloremeter and the GPS, we need to add the following Delegates to out interface.


@interface MyTutorialViewController : UIViewController


We also need to create some objects. Since location come with to values latitude and longitude, we need two UI objects to display them, also we need a locationManager which lets us to interact with the GPS and lets say an action method which we trigger from UI to clear the values and recheck the location. We need to create the action methods and UI outlets in the interface since our UI (xib) object will only interacts with this one. We also need another import to make use coreLocation libraries. The final code should look like this;


#import
#import

#define kThreshold 1.2
@interface MyTutorialViewController : UIViewController {
CLLocationManager *locationManager;
IBOutlet UITextField *latitude;
IBOutlet UITextField *longitude;
}
@property (retain, nonatomic) CLLocationManager *locationManager;
@property (retain, nonatomic) UITextField *latitude;
@property (retain, nonatomic) UITextField *longitude;

-(IBAction)clearContent:(id)sender;
@end


The first 2 lines are the import statements. Next we define a global constant kThreshold for checking the shake sthresholds. Next we have the CLLocationManager object which provides the GPS interaction. UI objects which are going to be binded to xib file are created with IBOutlet decleration. Here we created two UITextField objects, actually UILabel objects would be perfectly enough but to make use of different UI components I prefer to use editable text fields which we later define as uneditable. Next we need declare the @property lines for all thoe variables. I am not going to go in more detail with this, else this single post would become more complicated than a Objective-C book. Last we need to add the decleration of our action method, since again this would be something interacting with the UI -in this case receiving an action- we mark this with IBAction.

We are done with the .h file but we need to add a framework as a last step, if you expand the frameworks on the left pane you will notice there are some already imported frameworks.

Picture 6

Since we are going to make use of location services, we need to add to the framework associated with it. With a little help from google and some guess you can figure out its called CoreLocation.framework (you can also prefer to open the frameworks folder and try to find through them). Right click to your project, choose add and an existing framework.

Picture 7

Choose CoreLocation.framework, you will notice you can navigate through to see all available frameworks in this pane.

Picture 8and finally click add to add the framework to your project. With SDK 3 the option at the top of the window is dimmed. However if your version lets you to check this be sure that this option is not enabled. This is used to embed the framework which is already a part of the OS to your project (it is like embedding the rt.jar with your java application, simply you really don't need this unless you want to create pain to everyone who downloads your app).

Picture 9

Done, so lets go and finish the implementation, click MyTutorialViewController.m. Lets start with making use of the objects we defined in our interface. Objective-C has a different way to create getters and setters. You need to mark the objects you defined in the interface with the @synthesize keyword.


@implementation MyTutorialViewController

@synthesize locationManager;
@synthesize latitude;
@synthesize longitude;


Using predefined templates also provides some built in methods. If you scroll down you will notice methods which are commented out. most of those are ready to be used as you make them available.

Picture 4One of the most commonly used of them is viewDidLoad which is fired whenever the view associated with the controller is loaded. Uncomment that method and add the following;


- (void)viewDidLoad {
UIAccelerometer *accelerometer = [UIAccelerometer sharedAccelerometer];
accelerometer.delegate = self;
accelerometer.updateInterval = 0.1;
}


To explain what we did, we kindly ask iPhone to share the accelerometer with us and declare our class (self) as the delegate of that accelerometer. Last we provide an interval value, to tell the accelerometer how frequently we want to be informed about movements. Since accelerometer will think our class as a delegate which can handle the updates, it expects to fint out the methos where the values to be send. Scroll down to bottom of the file and just before the @end statement insert the following method.


- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration {
if (acceleration.x > kThreshold || acceleration.y > kThreshold || acceleration.z > kThreshold) {
//shaken go and subscribe to location service
self.locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
}


didAccelerate methos will be called by the accelerometer with the given interval and an acceleration parameter will be passed. What we do next is to check if any of the coordinates (x,y or z) goes higher than the given threshold. If it does than we initialize our locationManager object and declare our class as the delegate of it. As you should have noticed most of APIs in Objective-C are used via delegates. Last we need tell our locationManager that it can start updating the location info. Objective-C again offers a different syntax to send messages to objects as you see on line 7 (instead of dot notation). Again I am not going to go into detail in this.

To receive the location updates, we need to add the following method which the API is expecting to see in its delegate;


- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {

latitude.text = [[NSString alloc] initWithFormat:@"%g°", newLocation.coordinate.latitude];
longitude.text = [[NSString alloc] initWithFormat:@"%g°", newLocation.coordinate.longitude];

[locationManager stopUpdatingLocation];
}


didUpdateToLocation will be called and updates (the accuracy and intervals can be given as parameter however its not guaranteed that the GPS will respond those according to coverage) will be send via two parameters the newLocation and the oldLocation. Since what we care is the new location's attitude and longitude. We request those values as a new String object with the desired format and assign the to UI placeholders text values. Last, as we do not need any further updates once we receive the location (keep in mind you are using battery on a mobile devide) we kindly ask the locationManager to stopUpdatingLocation. Since our didAccelerate methos will reregister the location manager if the user shakes the phone.

We already had created an action method to clear the labels. Add the following for this purpose;


-(IBAction)clearContent:(id)sender{
latitude.text=nil;
longitude.text=nil;
}


We also wanted the user interface to rotate according to changes in the phone's orientation which is also already defined but commented out. Scroll, find shouldAutorotateToInterfaceOrientation method and uncomment it. We should let it return true for orientation changes, just change the return type. The method should look like the following;


- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return YES;
}


Ok finally, we do not have a garbage collector and we are on a mobile platform so we need to collect our own garbage. Scroll to find dealloc method which has already been defined and release all the objects we had created so far.


- (void)dealloc {
[locationManager release];
[latitude release];
[longitude release];
[super dealloc];
}


We are pretty done with coding now we need to build our interface via drag and drops and make necessary connections. You will be suprised that all of those can be done visually. Double click the MyTutorialViewController.xib to fire up the Interface Builder.

Picture 1Interface Builder consists of 4 different windows, which are consisted of different panes. tabs and options. Again I will not go all in detail just go straight and built the interface.

Picture 11

On the left window in the screenshot above, the window titled view is the view of your application, on the very right you can find the built in components provided for iPhone and in between there is a window where you can tweak the setting of your components you place on your view.

As you have noticed our view is gray, so lets plave a view component from components list. Scroll until you see view and simply drag and drop on top of your view.

Picture 12

Next place two labels, two text fields and a button to the view, you should notice guide lines will appear as you drag items. When you select an item you had replaced, the middle pane changes to properties of that item.

Picture 1

Your view should also be looking something like the image above. When you click on the labels you can change the text property by editing the test on the middle window. As you may have remember, we had used text fields to display the coordinates. However by nature those fields are editable and a default soft keyboard will appear when you focus on them. To stop this we need disable user interaction.

Picture 16

Be sure to uncheck 'User Interaction Enabled' for both of the text fields. You may also prefer to change other properties and see the effects. To check the layout in action you can save and go back to xCode to run your project.

Altough we have done coding and pretty finished visually, still we don't have any connection between them. We need to bind the visual components to IBOutlets we created in code. select the button, right click and drag to File's Owner icon in xib window as shown in the image below.

Picture 18

As you release the button, a popup will appear. You will notice the name of the action method you created before is listed. As you select the method and the visual component will be binded.

Picture 19Alternatively, you can select the button go to the middle window and select the second tab which show all avaible references of this outlet. You can click and drag touch up inside to again file's owner. Since you already did the first step this refernce should already be binded.

Picture 20

Next select View and go back to previous window bind the text fields the same way by dragging the connections to the UI components.Picture 21

If any of the steps, something is missing, can not be connected or unavailable go back and check again although iPhone development is heavily assisted, it is quite easy to skip something and once you skip it is hard to find out why something is missing.

Run the application, if you have chance to deploy it on a real phone do so since it is the only way to test the shake effect.

Picture 25

Actually there is a new shake API which came whith OS 3 and actually easier to implement than the one we did but for the sake of understanding the way Objective-C works I prefered to do it in the old way. Using the old way also gives you opportunity to work on OS 2 devices. However if we had chooe to use the new shake api we could be using the sake option provided with the simulator to test the shake action.

Rotota the phone and hold it in landscape mode, you will notice the screen changes the orientation.

Picture 24

Next if you have chance to deploy the app on a real device, or change the shake method with the new one provided with OS3 and use the shake command on the editor (again this option will not work with code given above but our code works fine on the phone and should give better understanding).

Picture 26For the last step, tap the button to clear the text fields.

It should be a little confusing, if you are new to Objective-C and xCode. Although it might look a bit confusing in the beginning, the structure, syntax and the APIs provided by Objective-C is not that hard and fun to use. Eventhough I didn't mention keep in mind there is talented full debugger built in xCode which can be life saving.

If you have troubles with understanding go and check basic Objective-C tutorials on Apple's site, since this post aims to show you the structure and how to make use of basic APIs of the Objective-C.