Blog Archives

iPhone Coding – Turbo Charging Your Apps With NSOperation

Introduction

So, let’s face it, MANY applications in the app store are “Clunky”.  They have jittery interfaces, poor scrolling performance, and the UI tends to lock up at times.  The reason? DOING ANYTHING OTHER THAN INTERFACE MANIPULATION IN THE MAIN APPLICATION THREAD!

What do I mean by this? Well, I am essentially talking about multithreading your application.  If you don’t know what is meant by multithreading, I suggest you read up on it and return to this post OR don’t worry about it because you don’t need much threading knowledge for this tutorial.  Let’s dig in and I’ll give you an example of the problem.

The Problem

When you create an application, the iPhone spawns a new process containing the main thread of your application.  All of interface components are run inside of this thread (table views, tab bars, alerts, etc…).  At some point in your application, you will want to populate these views with data.  This data can be retrieved from the disk, the web, a database, etc… The problem is: How do you efficiently load this data into your interface while still allowing the user to have control of the application.

Many applications in the store simply ‘freeze’ while their application data is being loaded.  This could be anywhere from a tenth of a second to much longer. Even the smallest amount of time is noticeable to the user.

Now, don’t get me wrong, I am not talking about applications that display  a loading message on the screen while the data populates.  In most cases, this is acceptable, but can not be done effectively unless the data is loaded in another thread besides the main one.

Here is a look at the application we will be creating today:

Let’s take a look at the incorrect way to load data into a UITableView from data loaded from the web.   The example below reads a plist file from icodeblog.com containing 10,000 entries and populates a UITableView with those entries.  This happens when the user presses the “Load” button.

Wrong (download this code here to see for yourself)

@implementation RootViewController
@synthesize array;
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    /* Adding the button */
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Load"
        style:UIBarButtonItemStyleDone
        target:self
        action:@selector(loadData)];
 
    /* Initialize our array */
    NSMutableArray *_array = [[NSMutableArray alloc] initWithCapacity:10000];
    self.array = _array;
    [_array release];
}
 
// Fires when the user presses the load button
- (void) loadData {
 
    /* Grab web data */
    NSURL *dataURL = [NSURL URLWithString:@"http://icodeblog.com/samples/nsoperation/data.plist"];
 
    NSArray *tmp_array = [NSArray arrayWithContentsOfURL:dataURL];
 
    /* Populate our array with the web data */
    for(NSString *str in tmp_array) {
        [self.array addObject:str];
    }
 
    /* reload the table */
    [self.tableView reloadData];
}
 
#pragma mark Table view methods
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.array count];
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                reuseIdentifier:CellIdentifier] autorelease];
    }
 
    /* Display the text of the array */
    [cell.textLabel setText:[self.array objectAtIndex:indexPath.row]];
 
    return cell;
}
 
- (void)dealloc {
    [super dealloc];
    [array release];
}
 
@end

“Looks good to me”, you may say.  But that is incorrect.  If you run the code above, pressing the “Load” button will result in the interface ‘freezing’ while the data is being retrieved from the web.  During that time, the user is unable to scroll or do anything since the main thread is off downloading data.

About NSOperationQueue And NSOperation

Before I show you the solution, I though I would bring you up to speed on NSOperation.

According to Apple…

The NSOperation and NSOperationQueue classes alleviate much of the pain of multi-threading, allowing you to simply define your tasks, set any dependencies that exist, and fire them off. Each task, or operation, is represented by an instance of an NSOperation class; the NSOperationQueue class takes care of starting the operations, ensuring that they are run in the appropriate order, and accounting for any priorities that have been set.

The way it works is, you create a new NSOperationQueue and add NSOperations to it.  The NSOperationQueue creates a new thread for each operation and runs them in the order they are added (or a specified order (advanced)).  It takes care of all of the autorelease pools and other garbage that gets confusing when doing multithreading and greatly simplifies the process.

Here is the process for using the NSOperationQueue.

  1. Instantiate a new NSOperationQueue object
  2. Create an instance of your NSOperation
  3. Add your operation to the queue
  4. Release your operation

There are a few ways to work with NSOperations.  Today, I will show you the simplest one: NSInvocationOperation.  NSInvocationOperation is a subclass of NSOperation which allows you to specify a target and selector that will run as an operation.

Here is an example of how to execute an NSInvocationOperation:

NSOperationQueue *queue = [NSOperationQueue new];
 
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
    selector:@selector(methodToCall)
    object:objectToPassToMethod];
 
[queue addOperation:operation];
[operation release];

This will call the method “methodToCall” passing in the object “objectToPassToMethod” in a separate thread.  Let’s see how this can be added to our code above to make it run smoother.

The Solution

Here we still have a method being fired when the user presses the “Load” button, but instead of fetching the data, this method fires off an NSOperation to fetch the data.  Check out the updated code.

Correct (Download the source code here)

@implementation RootViewController
@synthesize array;
 
- (void)viewDidLoad {
    [super viewDidLoad];
 
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Load"
       style:UIBarButtonItemStyleDone
       target:self
       action:@selector(loadData)];
 
    NSMutableArray *_array = [[NSMutableArray alloc] initWithCapacity:10000];
    self.array = _array;
    [_array release];
}
 
- (void) loadData {
 
    /* Operation Queue init (autorelease) */
    NSOperationQueue *queue = [NSOperationQueue new];
 
    /* Create our NSInvocationOperation to call loadDataWithOperation, passing in nil */
    NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self
        selector:@selector(loadDataWithOperation)
        object:nil];
 
    /* Add the operation to the queue */
    [queue addOperation:operation];
    [operation release];
}
 
- (void) loadDataWithOperation {
    NSURL *dataURL = [NSURL URLWithString:@"http://icodeblog.com/samples/nsoperation/data.plist"];
 
    NSArray *tmp_array = [NSArray arrayWithContentsOfURL:dataURL];
 
    for(NSString *str in tmp_array) {
        [self.array addObject:str];
    }
 
    [self.tableView performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:YES];
}
 
#pragma mark Table view methods
 
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return [self.array count];
}
 
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
 
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
    [cell.textLabel setText:[self.array objectAtIndex:indexPath.row]];
 
    return cell;
}
 
- (void)dealloc {
    [super dealloc];
    [array release];
}

As you can see, we haven’t added much code here, but we have GREATLY improved the overall user experience.  So, what did I do exactly?

  1. Moved all of the processing (downloading) code from the loadData method to another method that could be run asynchronously
  2. Created a new instance of NSOperationQueue by calling [NSOperationQueue new]
  3. Created an NSInvocationOperation to call our method loadDataWithOperation
  4. Added the operation to the queue
  5. Released the operation
  6. When the Data has been downloaded, we reload the table data in the main thread since it’s a UI manipulation

One thing to note here is we never actually tell the operation to run.  This is handled automatically in the queue.   The queue will figure out the optimal time run the operation and do it for you.

Now that you have your downloading and processing in a separate thread, you are now free to add things such as a loading view.

I will be expanding on this tutorial in the coming week and showing you how to cache data and display old data to the user while the new is loading.  This is a popular technique used in many Twitter and News applications.

That concludes today’s tutorial.

Post questions in the comments or Ping me on Twitter.

Happy iCoding!

iPhone Coding Recipe – Shortening URLs

I had some a to shorten URLs for an in-application Twitter client I’m working on and thought I would share my simple solution with you guys.

It’s actually pretty straight forward and can be done in 1 line of code.  I have broken it up into several for clarity.

NSString *url    = @"http://brandontreb.com";
NSString *apiEndpoint = [NSString stringWithFormat:@"http://api.tr.im/v1/trim_simple?url=%@",url];
NSString *shortURL = [NSString stringWithContentsOfURL:[NSURL URLWithString:apiEndpoint]
		 encoding:NSASCIIStringEncoding
		 error:nil];
NSLog(@"Long: %@ - Short: %@",url,shortURL);
 
// Outputs Long: brandontreb.com - Short: tr.im/MRDd

Pretty easy huh?

The magic here is in a method that Apple gave us as part of NSString. This method is called stringWithContentsOfURL. It will easily allow you to grab the text of any remote source.

I have used Tr.im as an example here because their service is very easy to use and has little overhead.  I would have used bit.ly but they return a JSON string which would then have to be parsed.  Tr.im’s trim_simple service simply outputs the string of the shortened URL.

Sure Twitter may shorten links for you automatically, but what if you want to use a custom service? Or,…wait for it… use it for something other than Twitter (please post in the comments if you do. I would love to hear some other uses :) )

Questions? Comments? Complaints?

Happy iCoding

UITextField – A Complete API Overview

The UITextField is probably one of the most commonly used UI controls on the iPhone. It is the primary method of user input via the keyboard and provides a great deal of additional functionality.

With the success of our las API tutorial on NSArray, I thought I would do another walkthrough, this time on UITextField. I will be explaining all of the properties for it as well as bringing up some functionality that you may not have known about.

Text Attributes

The attributes have to do with the actual text inside of the UITextField.

text The text displayed in the UITextField
placeholder The text that gets displayed prior to the user entering in anything. This text is usually a lighter color than the primary text to denote that it will be replaced.
font The font of the text to be displayed. You can set it like this
textColor The color of the text that is displayed
textAlignment How the text is aligned in the UITextField. The possible values for this are UITextAlignmentLeft, UITextAlignmentRight, UITextAlignmentCenter

Here are some examples of using these properties.

// Setting the text
[myTextField setText:@"This is some text!"];
 
// Setting the placeholder
[myTextField setPlaceholder:@"Type text here"];
 
// Setting the font.
[myTextField setFont:[UIFont fontWithName:@"Times New Roman" size:14]];
 
// Setting the text color
[myTextField setTextColor:[UIColor blueColor]];
 
// Setting the text alignment
[myTextField setTextAlignment:UITextAlignmentCenter];

Here is what the UITextField would look like after we update these properties.


Adjusting the size of the text in the UITextField

The text displayed in our UITextField can be dynamically sized based on the width of the UITextField. The benefit of this is all of the text being typed will be visible on the screen. It will shrink the text down until it reaches the default font size of 17. So, for this to make sense, you must set the font size of the UITextField to something larger than 17.

adjustsFontSizeToFitWidth Boolean value denoting whether to fit the font size to the width of the UITextField.

Here is an example of using these properties.

[myTextField setFont:[UIFont fontWithName:@"Times New Roman" size:30]];
[myTextField setAdjustsFontSizeToFitWidth:YES];

Here are some screenshots of the text shrinking when typing in the UITextField.


Managing the editor’s behavior

These two properties are pretty straight forward.

editing Read-only boolean value letting you know if the user is currently editing the UITextField
clearsOnBeginEditing Clears the text in the field every time the user begins to edit it.

Not very exciting and probably doesn’t even deserve an example…

Setting the view’s background appearance

This group of properties defines how the UITextField will look. If you have ever seen a fancy input box, this is how they are doing it.

borderStyle Defines the type of border for the UITextField. Possible choices are UITextBorderStyleNone, UITextBorderStyleLine, UITextBorderStyleBezel, and UITextBorderStyleRoundedRect. The default is UITextBorderStyleNone.
background A UIImage representing the background image of the UITextField when it’s enabled. If this field is altered the borderStyle property is ignored.
backgroundDisabled A UIImage representing the background image of the UITextField when it’s disabled.

Here is are some example of using each of the border styles

// Border Style None
[myTextField setBorderStyle:UITextBorderStyleNone];

// Border Style Line
[myTextField setBorderStyle:UITextBorderStyleLine];

// Border Style Bezel
[myTextField setBorderStyle:UITextBorderStyleBezel];

// Border Style Rounded Rect
[myTextField setBorderStyle:UITextBorderStyleRoundedRect];

The border style is not terribly exciting. However, you can really spruce up your UITextFields using the background property. Here is an example of setting the background property to this image.

myTextField.textAlignment = UITextAlignmentCenter;
myTextField.textColor = [UIColor whiteColor];
myTextField.borderStyle = UITextBorderStyleNone;
myTextField.background = [UIImage imageNamed:@"bg.png"];

and the result… Looks pretty good ehh? One GOTCHA that I want to point out here is, to get the background property to work correctly, you must set the boderStyle to anything other than UITextBorderStyleRoundedRect. Otherwise, the default UITextField will be displayed.
Setting the view’s background appearance

Managing Overlay Views

Another interesting way of customizing your UITextFields is to use an overlay. UITextField offers a left and right overlay for your UITextFields. Here are the properties:

clearButtonMode The circled X that gets displayed when typing. Used to clear out the text. Possible values: UITextFieldViewModeNever, UITextFieldViewModeWhileEditing, UITextFieldViewModeUnlessEditing, UITextFieldViewModeAlways
leftView The view that appears to the left inside a UITextField. This could be something like a magnifying glass for search.
leftViewMode Works like clearButtonMode, but toggles the leftView.
rightView Same as leftView, except it aligns to the right.
rightViewMode Same as leftViewMode but controls the rightView

Let’s take a look at how adjusting the leftView works:

UIImageView * myView = [[ UIImageView  alloc ]  initWithImage :
		[UIImage  imageNamed : @"wordpress.png" ]];
[myTextField  setLeftView :myView];
[ myTextField   setLeftViewMode: UITextFieldViewModeAlways];
[myView release ];

As you can see, the text aligns after the image. This is a very simple way to really spruce up your UITextFields.

The last thing we are going to discuss is showing and hiding the keyboard.

Showing and Hiding The Keyboard

To show the keyboard:

[myTextField becomeFirstResponder];

To hide the keyboard

[myTextField resignFirstResponder];

Well, I hope you have enjoyed this tutorial on the UITextField.  I would love to see links to some interesting custom UITextFields in the comments, so please post them.  Thanks for reading and happy iCoding!

Code Snippet – Quickly Find The Documents Directory

As many of you may have seen by now, there are quite a few ways to find the documents directory on the iPhone.  For those of you who don’t know, the documents directory of an app is the location where you should save your application data.  While finding the documents directory is a trivial task, it is very important when coding most applications.  Apple has provided quite a few ways for resolving the path to this directory.

If you read through some of Apple’s sample code, you will see their code to do this is generally 3 or 4 lines long.  This seems like a lot of code to perform such a simple task.  Here is a nice one-liner for you to use in your applications.

NSString * docs = [NSHomeDirectory()
						   stringByAppendingPathComponent:@"Documents"];

It’s clean and concise. Happy iCoding!

Objective-C Tutorial: NSArray

Here at iCodeblog, we have been showing you guys how to create many different types of applications from the ground up.  Well, today I decided to do something different and get down to some of the nitty gritty of a structure we rely heavily on in objective-C.

The NSArray is a huge workhorse that we use quite frequently without even thinking about it.  The NSArray class isn’t just your ordinary array.  Not only does it provide random access, but it also dynamically re-sizes when you add new objects to it and has many methods to make our lives easier.  While I won’t go over every method in NSArray (there are quite a few), I will discuss some of the more important ones that are most commonly used.  Let’s take a closer look at this class.

Factory Methods

Factory methods are static methods that build new instances of NSArrays from given parameters and return them.  The table below details on all of the factory methods for the NSArray class.

+ (id)array Creates and returns an empty array
+ (id)arrayWithArray:(NSArray *)anArray Creates and returns an array containing the objects in another given array.
+ (id)arrayWithContentsOfFile:(NSString *)aPath Creates and returns an array containing the contents of the file specified by a given path. * The file must be of type .plist for this method to work
+ (id)arrayWithContentsOfURL:(NSURL *)aURL Similar to arrayWithContentsOfFile except it will load the .plist remotely from a given website. This would be a very simple way to get data from a web service.
+ (id)arrayWithObject:(id)anObject Creates and returns an array containing a given object. This will just be a 1 element array
+ (id)arrayWithObjects:(id)firstObj, … This method is used when you have multiple objects on hand and want easily insert them into an array. Make sure the last element you add is nil or this method won’t work.
+ (id)arrayWithObjects:(const id *)objects count:(NSUInteger)count Creates and returns an array that includes a given number of objects from a given C array.

Here is some example usage of building NSArrays with these factory methods…

// I am using strings, but you can add just about any object to an NSArray
 
// Creates an NSArray with one object
NSArray  * myArray = [NSArray arrayWithObject:@"foo"];
 
// Creates an NSArray with multiple objects. Don't forget to add nil as the last object
NSArray  * myArray2 = [NSArray arrayWithObjects:@"foo",@"bar",@"baz",nil];
 
// Creates an NSArray from another NSArray
NSArray * myArray3 = [NSArray arrayWithArray:myArray2];
 
// This will create an NSArray from data on iCodeBlog.  Go ahead and try it out, this file exists on our servers and contains valid data.
NSArray * myArray4 = [NSArray arrayWithContentsOfURL:[NSURL URLWithString:@"http://icodeblog.com/wp-content/uploads/2009/08/foo.plist"]];

You can also choose not to use factory methods and just use the normal NSArray initializers. They are pretty much the same as the factory methods only you do the allocation yourself. An example of this might be:

NSArray * foo = [[NSArray alloc] initWithObjects:@"foo",@"bar",@"baz",nil];

Accessing The NSArray

Apple has provided us with many great methods for getting data out of an NSArray as well as information about it.  Here is a table of methods and their descriptions.

- (BOOL)containsObject:(id)anObject Returns true if a given object is found in the array, false otherwise
- (NSUInteger)count Returns the size of the array
- (id)lastObject Returns the last object in the array (the one with the highest index)
- (id)objectAtIndex:(NSUInteger)index Gives you random access to the array. Returns the object at a given index.

These are just a few of the accessor methods of NSArray and most likely the only ones you will need to use.

Searching The Array

If you are looking for the index of an object in an array, there is really only one method you need to use. That method is indexOfObject: . An example of usage might be:

NSString * f = @"foo";
NSString * b = @"bar";
NSString * z = @"baz";
NSArray  * myArray2 = [NSArray arrayWithObjects:f,b,z,nil];
NSInteger idx = [myArray2 indexOfObject:b];
// This would return 1 (since NSArrays are 0 - indexed)

This is much cleaner code than looping over the entire array to find an object. It’s probably more efficient too as Apple is pretty clean in how they code things.

Sending Message To Objects In The Array

Ok, so this is pretty cool.  Say you have an array of objects that all need to do something.  For example, an array of bullets, and you want all of the bullets to move forward one pixel. Rather than looping over each bullet object in the bullets array and calling [bullet move], you can do it in one method call on the array.  This method is called – (void)makeObjectsPerformSelector:(SEL)aSelector and here is an example of usage.

// Lets pretend the bullet object has a method called move
// and there is an array of 50 bullets
 
[bullets makeObjectsPerformSelector:@selector(move)];

And that’s it… The move method will now be called on every bullet object in the array with this one method call. Very clean and easy to use. Now, if the method you want to call takes an argument, there is the – (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject method. This will allow you to pass an object to each method being called.

Sorting Arrays

Apple has provided us with some very slick ways to sort an NSArray. I will not go into too much detail here as I have a full tutorial planned dedicated to sorting arrays. For now, I will just show you how to sort an NSArray of NSStrings. The method we will be using is sortedArrayUsingSelector. Here is the example usage.

NSArray *sortedArray =
    [myArray2 sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)];
    // This will return a sorted array that looks like [@"bar",@"baz",@"foo"]

You can plug in any of the string compare functions there to compare the strings. If you are sorting an NSArray of custom objects (like users), you can overwrite the compare method for that object and pass compare in for the selector. Just make sure you remember the : at the end of the method.

Looping Through Arrays

So if you are reading this, I assume you have seen a for loop before. ex: for(int x=0; x < … This would most likely not be a good way to enumerate an NSArray as Apple has provided a much nice way for doing so. There are some situations where this method would be preferred (like when u need to calculate the indices or something). But in most cases, you will want to use the special for loop provided by Apple. If you have ever coded PHP, it is much like the foreach loop. Here is an example:

for(NSString * myStr in myArray2) {
    NSLog(myStr);
}

This will loop over myArray2 and pring each element in that array. Very clean and efficient.

Saving Arrays For Later

There is an Array of ways to save data on the iPhone (Pun intended). One way is to simply dump the array to a file. This will write out the NSArray to a plist file on disk that can be loaded later using the arrayWithContentsOfFile method of NSArray. That method is rightfully named writeToFile. The example usage for it is as follows.

NSArray  * myArray2 = [NSArray arrayWithObjects:@"foo",@"bar",@"baz",nil];
[myArray2 writeToFile:filePath atomically:YES];

And there you have it! You are probably wondering what the atomically variable means. Me too… Just kidding. If YES, the file will be written to a temp location and then renamed to its final destination. This is put in place to guarantee that the file won’t be corrupted even if the system crashes (rename will be much faster than write). Why is this needed you ask? Consider this scenario.

You want to write a huge array to disk to save it for later. When your app starts it checks the disk to see if this file exists, if it does it loads an NSArray from it. If it doesn’t it creates a new NSArray. Say the last time the app ran, the system crashed while the file was being written. If atomically was set to NO, the original file would be corrupt and now the application would be loading corrupt data every time it starts from now on (most likely causing a crash). However, if you set atomically to YES, the temp file would get corrupted and the app would never see it. That way, the next time the app starts it will create a new fresh NSArray and all will be good.

Good rule of thumb, set atomically to YES.

That concludes our tutorial on NSArray. If you have any questions or comments, feel free to post them in the comments section or write me on twitter.

WP Like Button Plugin by Free WordPress Templates