BuySellAds.com

Until Dec 3rd, 44% off all Manning books, including Barcodes with iOS! Promo code: mobicftw
Our DNA is written in Objective-C
Jump

Getting Standard Paths

These are the standard methods of getting the app directory and the documents directory. Remember that on the iPhone the app as well as the documents directory are “sandboxed” i.e. each app has it’s own two directories.

Here’s how you can get the path for files in your app bundle/directory:

// a regular file
NSString *plistPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"File.plist"];
 
// this works also for localized files, gets the best matching localized variant
NSBundle *thisBundle = [NSBundle bundleForClass:[self class]]; // alternate method to get bundle
NSString *path = [thisBundle pathForResource:@"File" ofType:@"plist"];

One common mistake is to pass a file name including extension in the first parameter  of the pathForResource method. This won’t work, pass only the name without extension as first parameter and only the extension as second parameter.

And to get something from the documents directory you can do it like this:

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:@"File.plist"];

You’ll find yourself copy/pasting these lines all the time basically for every file that you are reading or writing.

How to Start Developing

I keep getting questions about how to best get into coding apps for the iPhone. Do I recommend books? How should one go about this?

I have one recommendation: Only steel good stuff. Download all the samples you can find on the Apple site and look them through. For most of the basic tasks you can find code to steal there, well commented.

But most importantly I recommend you get a mentor. Somebody who is slightly ahead of you in the objC programming game. But not too far ahead to feel annoyed by your questions… ;-)

And that’s the last recommendation: learn to ask the right questions. Because if you ask the right questions you will find that …

40% get answered by Apple documentation
40% get answered by Google: somebody has answered the question in his blog or in a forum
15% get answered by somebody more experienced than you directly if you ask
4% you have to discover for yourself … and then hopefully you document your discovery on your blog
1% are bugs that you have discovered by accident :-) Those you please submit to Apple for them to fix.

When I got started with coding objC for iPhone there was next to no useful literature. And now that I have written half a dozen apps and managed to get 4 into the store I don’t think I would ever need a book. Once you know how to read and understand objC code there is nothing really that you need for reference except the sources I mentioned above.

One more thing: try to develop a network of friends who all are sharing coding for iPhone as the same hobby. Of 10 questions I asked in various forums I only got around 3-5 answered usefully. If you remember the SDK 3.0 presentation, you know that more than half of current iPhone developers are new to the platform! So you are in the same boat as most of us. Be strong! Don’t give up!

Key-Value-Coding

Simon asks:

I would like to loop through variables whose names end with numbers 0 to 5. What is the correct syntax for the i? I have tried variable[i], variable + i, without success

I know two methods to achieve this:

1. Put your objects into an array and then enumerate or for-loop over those

// e.g. in awakeFromNib
playerNameLabels = [[NSArray arrayWithObjects:player1NameLabel, player2NameLabel, player3NameLabel, nil] retain];
 
for (i=0;i<3;i++)
{
     UILabel *nameLabel = [playerNameLabels objectAtIndex:i];
     // do something the the nameLabel
}

2. Use the Key-Value-Coding approach.

// in this object's header the strings are defined, here we intialize them
string1 = @"string1";
string2 = @"string2";
string3 = @"string3";
 
int i;
 
for (i=1;i<3;i++)
{
     NSString *aName = [NSString stringWithFormat:@"string%d", i];
     NSString *oneString = [self valueForKey:aName];
     NSLog(@"string %d is '%@' and has length %d", i, oneString, [oneString length]);
}

The dangerous thing about the latter might be that you have construct precisely the right name for the value. Or else you will get an exception:

*** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<selectortestAppDelegate 0x524e80> valueForUndefinedKey:]: this class is not key value coding-compliant for the key string0.’

But actually the second approach is how Cocoa does most KVC internally.

LuckyWheel Garners Favorable Reviews

It’s only a couple of days since LuckyWheel was approved for sale on the App Store.

The first few sites that linked to the store where clearly automatic robots. iTunes gets its information in XML plist format and a couple of sites came into existence that do nothing but parse this information

I don’t know what effect those will have on sales. They might just be a modern form of spam blogs.

But what really made my day was finding a review on FingerGaming. LuckyWheel went public on the same day as “Wheel of Fortune” by Sony which aims to mimic the mechanics of the TV show by the same name. In contrast we did not try to copy something, but create a unique approach. Make a game that uses the strength of the touch interface and fill it up to the brim with proverbs in English, German, Spanish, French, Italian and Dutch.

They say:

“LuckyWheel additionally boasts a feature that Wheel of Fortune doesn’t include in any capacity — a fully fledged German-language option, complete with a localized puzzle selection full of German proverbs.”

“[...] LuckyWheel offers gameplay variety that Wheel of Fortune could never dream of.”

“LuckyWheel is a good protest purchase if you want to voice your disappointment in Wheel of Fortune[...]“

Next up in mostly positive feedback was iPhone App Reviews.net. This was the only site that actually also allowed for giving away a couple of free promo codes to their mostly US-based readers.

They say:

[...] LuckyWheel can really be pretty fun when you’re with a friend or two and having a little pass-n-play competition.

[...] Drobnik is committed to developing LuckyWheel to be as good as (or better than?) Sony’s game

Everyone who likes this kind of word puzzle but isn’t married to the TV format, get LuckyWheel. Despite its shortcomings, LuckyWheel is decent for $1.99 and hopefully it’ll grow into something that can really compete with an established franchise.

Just as welcome as professional reviews are comments by the users themselves

Chris: “I like the game. I have also tried Wheel of Fortune from Sony and have more fun playing LuckyWheel. Not because it is now free. I hope that there are more people who think the same.”

Peter: “I have also tested both games and have come to the result that LuckyWheel is the better one. Surely the graphics are not as beautiful, but why spend $4.99. So why not support LuckyWheel. Maybe a spin button would be nice, but I like spinning the wheel rather.”

We still have some promo codes available, valid on the US app store which we’ll happily provide to anyone who would like to write a review about any of our applications.

Also note that the next version has already been submitted to Apple and increases the guessing fun to all of these languages: English, German, Italian, Spanish, French and Dutch.

Drawing Text in Graphics Contexts

Sometimes you might need to draw text instead of just using a comfortable UILabel. For example if you want to show it a a specific angle. This takes a lot of code, but once you understand it, it’s just copy and paste.

- (void)drawRect:(CGRect)rect {
	CGContextRef ctx = UIGraphicsGetCurrentContext();
 
	CGContextSetRGBFillColor(ctx, 1, 0, 0, 1);   // inside red
	CGContextSetRGBStrokeColor(ctx, 0, 1, 0, 1);  // outline green
	double text_angle = -M_PI/4.0;  // 45 Degrees counterclockwise
 
	CGAffineTransform xform = CGAffineTransformMake(
		cos(text_angle),  sin(text_angle),
		sin(text_angle), -cos(text_angle),
		0.0,  0.0);
 
	CGContextSetTextMatrix(ctx, xform);
	CGContextSelectFont(ctx, "Helvetica", 20.f, kCGEncodingMacRoman);
	CGContextSetTextDrawingMode(ctx, kCGTextFillStroke);
	CGContextShowTextAtPoint(ctx, 100, 100, "Test Text", 9);
}

Of special importance is the application of a tranformation matrix via CGContextSetTextMatrix. Without it, your text will be drawn upside down. This is a feature, not a bug, because internally the iPhone still wishes that (0,0) is in the lower lefthand corner, but for most UIView-releated uses this is the “right way around”, i.e. (0,0) in the upper lefthand corner.

Angle of a Vector

When dealing with a multi-touch rotation I was searching for a simple method to calculate the angle of two fingers. Well, actually more precisely the angle between the vector from one finger to the second finger and the horizontal X Axis.

While I was still searching my friend Christian Pfandler came up with this function:

- (CGFloat)angleFromPoints:(CGPoint)pos1 otherPoint:(CGPoint)pos2
{
    CGPoint vector = CGPointMake(pos1.x-pos2.x, pos1.y - pos2.y);
    double angleCalc;
    if (vector.y &lt; 0)
    {
        // upper Half
        angleCalc = atan2(-vector.y,vector.x);
    }
    else
    {
        angleCalc = atan2(vector.y,-vector.x)+M_PI;
    }
 
    return angleCalc;
}

The regular atan(x) function seem to have a limitation by only returning values between 0 and +/- PI. Because of this there is also the atan2(y,x) function which is orders of magnitude more useful. To get a continuos angle for the full circle we have to treat the upper half and the lower half seperately though. I found this here.

I don’t quite understand it, but it seems to work. :-) Please keep in mind though that like all angles in Cocoa Touch this is in radians as well. So not 0…360, but 0…2*PI. If you know an even simpler method, please let me know.

iPhone OS 3.0 for Developers

Today the typical dev-geek was glued in front of his web browser to get as much information as possible on the pending release of the next version of the operating system that powers all touch-enabled devices under the Apple brand. I stuck with the major two, Gizmodo and Engadget, especially because those two by now have mastered the art of interspersing illustrative photographs amongst concise journalistic commentary.

Apple claims that there are 1000 new API’s coming in 3.0 that they have been busily developing over the last year. An impressive number of news by any rate and it was put to the test by the scrutiny of many an iPhone user who threatens to leave the platform for Android because of the lack of cut&paste, stereo bluetooth, tethering, background processes and the like.

Most of those deficits will be remedied in OS 3.0, or so it seems. With an Apple twist. Background processes, no way. But amazing notification infrastructure, that is nicer to stand-by times anyway, yes. Pay no attention to the man behind the curtain! Just watch and be amazed at the magic with the Apple.

As a developer I paid really close attention to what new features might work in my existing or future apps. The single most important thing I found was cut&paste. As simple as this might sound, even “standard”, as numerous are the uses I can imagine for it.

The guys on stage kept dealing out new APIs and features as if they where handing out candy.

  • Turn-based navigation will be possible … if the creator brings his own maps. Garmin, where are you?
  • “Stereo Bluetooth” aka. A2DP. Finally wireless music to my ears.
  • MMS, send voice memos, locations, vCards to other phones
  • “Spotlight” a seperate screen that searches everything on the device, even apps
  • Multiple Calendars with synching, especially noteworthy: CalDAV
  • Voice Memos, also with an external Microphone
  • Cut and Past, Shake to Undo. Copy full paragraphs e.g. from Safari
  • Push Notifications will finally enable a slew of multi-system IMs
  • Much improved Video Streaming capability with automatic best rate adaption

Lots of new APIs in OS 3.0

The bits that I thought most interesting to developers:

  • in-app e-mail support, maybe the end of the attachment shortcoming?
  • Shake API, no longer do I have to code this myself?
  • access to the users music library on the phone, probably also the voice memos
  • use the push notification API to invite other people to activites, e.g.games
  • USB/Bluetooth external-device API might allow for interesting add-on hardware possibilities
  • In-App Purchaes (iAP)! From within the app you can sell new game levels or other content
  • Access to the Proximity Sensor
  • Battery information API – maybe apps can now be smarter about how much CPU and energy they use
  • In-Game Voice – wow, about an online-multiplayer LuckyWheel that lets people talk to each other?
  • And many minor tweaks to existing classes UIAlertView, UITableView, table cells, etc.

Still there were so many more buzzwords that did not get treated with an explanation. Or do you know that those are?

  • Core Data – a new API for handling data? A nicer way to interact with Sqlite?
  • Localized Collation – Digital Alphabet Soup?
  • GPS Lingo – are they going to put even cooler GPS buzzwords into the SDK?

"We'll be back soon" says Apple while the update the developer site.

It still remains to be seen behind all these cool announcements how much liberty and creativity Apple will actually permit it’s developers to have. All these new features are really quite overwhelming for us small-time developers and it remains to be seen who of us can embrace them quickly.

Did anybody get to 1000 counting new announcements? I did not, but I am still excited.

It was also announced today that there would be a BETA available for paying developers. So far I cannot report anything about that because we are greeted with the usual “We’ll be back soon” because most likely the developer site is totally overrun by people hungry for more info.

In summary I am glad that Apple today showed that they are taking the touch platform seriously and they are willing to put in extra work to catch up to other platforms that boast many features where the iPhone fell behind. I feel reassured that I can relax and stick to developing for the iPhone because with all those shortcomings finally getting remedied the market of my potential customers will continue to grow for the foreseeable future.

GeoCorder 1.0.2

<a href="http://www.cocoanetics.com/index.php/geocorder/"GeoCorder 1.0.2 is now publicly available.

New Features:

  • added option to continue a recording
  • added icon for tracks in track overview

Fixes:

  • Changed all trackpoint times to UTC as per GPX spec
  • removed HDOP and VDOP digits right of comma
  • added proper time sorting of trackpoints in exported tracks

Localization Workaround

Trebor asks:

“When I add a new localization, there are just a few choices:

English

Japanese

French

German

I thought that the system needed “fr” to know it is French and “de” to know it is German?

Will it recognize the word “German” and know that is the same as a de.lprog?

If the answer is yes, does it know the full names of all languages?

For example, if I want to add Chinese, can I call the localization “Chinese” or do I need to call it zh?”

I believe the following to be true. Xcode is still putting in English as first localization if you make a file localizable. This works, but the “modern way” to do things (or so I read) is to use the two letter iso codes. The problem is, that if you remove the English then you can never add any new localization. That’s a bug in Xcode.

So I do it like this:

  • create the file
  • make it localizable
  • add localizations for all locales you require, including English: en, de, it, es, fr, nl
  • LEAVE English empty, but put English into the “en” locale

Do not remove the “English”, because then XCode will not allow you to add new localizations in the future.

NSArray Sorting

If you need the contents of an NSArray in a different order than they where entered into the array you have several options available. By far the simplest is to use descriptions if you have a standard data type you wish to sort by. You first set up an array with the names of fields to sort by. Then you sort.

// first you need to set up the descriptor
 
NSSortDescriptor *firstDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"firstField"
							 ascending:YES] autorelease];
NSSortDescriptor *secondDescriptor = [[[NSSortDescriptor alloc] initWithKey:@"secondField"
							ascending:YES] autorelease];
 
NSArray *sortDescriptors = [NSArray arrayWithObjects:firstDescriptor, secondDescriptor, nil];
NSArray *unsortedObjects = [NSArray arrayWithObjects:oneObject, secondObject, nil];
 
// new and shiny sorted array
NSArray *sortedObjects = [unsortedObjects sortedArrayUsingDescriptors:sortDescriptors];

A little more involved but way more powerful is the method of using a selector. Here you add a function to your special class that you are keeping in the aray which takes care of deciding which of two object comes first: self or the other one.

Note that the sort method needs to be part of the class itself. You can make your sorting logic as complex as you like to using multiple sort methods. Cocoa Touch calls this method every time it needs to know the order of two objects. Having access to all the other object’s properties you can have your function return NSOrderedAscending, NSOrderedDescending or NSOrderedSame depending on what order you determine to be correct.

//  MyOwnClass sort function
- (NSComparisonResult)compareByTime:(MyOwnClass *)otherObject
{
	NSTimeInterval diff = [self.timestamp timeIntervalSinceDate:otherObject.timestamp];
	if (diff&gt;0)
	{
		return NSOrderedDescending;
	}
 
	if (diff&lt;0)
	{
		return NSOrderedAscending;
	}
 
	return NSOrderedSame;
}

Once you have it set up like this sorting is as easy as:

// original unsorted array
NSArray *unsortedObjects = [NSArray arrayWithObjects:oneObject, secondObject, nil];
 
// new and shiny sorted array
NSArray *sortedObjects = [unsortedObjects sortedArrayUsingSelector:@selector(compareByTime:)];

Here is another example that uses the compare method that’s built into NSString:

NSArray *unsortedArray = [NSArray arrayWithObjects:@"String3", @"String2", @"String1", nil];
NSArray *sortedArray = [unsortedArray sortedArrayUsingSelector:@selector(compare:)];

Sorting is very simple with the two methods shown above. If you just want simple sorting then use descriptors. If you need more control over the sorting method then define your own sorting method.