Ad

Our DNA is written in Swift
Jump

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.

Retain, Release … repeat

In Objective-C you have to make sure that you match your alloc and retains with the appropriate number of release calls. Otherwise you might be leaking objects. Here’s an example that I faced today.

The normal pattern if you create an object that you then add to an array is the following:

Player *tmpPlayer =  [[Player alloc] initWithName:player1Name.text color:[UIColor greenColor]];
[tmpArray addObject:tmpPlayer];
[tmpPlayer release];

Because adding an object to an NSArray causes the object to be retained you have to religiously follow any addObject with a release. You only want to keep the object retained once because when you later release the array, the array in turn releases all it’s objects and if the retain count is higher then 1 then these objects will leak. I.e. their dealloc won’t be called because they still have a retain count higher than zero.

To visualize the dance of alloc, init, retain, release and dealloc you could do the following:

- (id) initWithName:(NSString *)aName;
{
		if (self = [super init])
		{
			self.name = aName;
			NSLog(@"player %@ init, retain count is now %d", name, [self retainCount]);
 
			return self;
		}
 
	return nil;
}
 
- (void) retain
{
	NSLog(@"player %@ retain, retain count is now %d", name, [self retainCount]+1);
	[super retain];
	// cannot log here, causes crash
}
 
- (void) release
{
	NSLog(@"player %@ release, retain count is now %d", name, [self retainCount]-1);
	[super release];
	// cannot log here, causes crash
}
 
- (void) dealloc
{
	NSLog(@"player %@ dealloc", name);
	[name release];
	[super dealloc];
}

As you can see it is easy to override retain, release and dealloc and inject an NSLog to output the current retainCount. But don’t leave those log statements in the final product! NSLog are executed even if you strip all other debug info via a release build and I have seen them dramatically decrease your app’s performance.

In my case today I had forgotten the release after the addObject and thus the Instruments tool showed a couple of bytes leak that I spent an hour looking for in the wrong place.

Localization Woes

Adding additional languages to your iPhone app is extremely easy. Below I’ll show you how. But there is one thing that the documentation did not mention, that also cost me a couple of hours.

Step 1: Change all strings to be localizable

You need to go through your code and everywhere you want a string to be localizable you need to change it to use the NSLocalizedString macro. This marks this string to be localized. Also if no localization can be found then the string returned is identical to the first parameter.

// without localization
player1Name.placeholder = @"Player 1";
 
// with localization
player1Name.placeholder = NSLocalizedString(@"Player 1", @"Player View");

The second parameter is called “comment”, but it’s actually sort of a context for the “Player 1” token. I would use this to uniquely identify the view where it’s used. 

Step 2: Generate your Localizable.strings file

The SDK comes with a very useful tool genstrings. This tool scans your code for all instances of NSLocalizedString and pulls these together into a UTF-16 strings file. This file you can send to a translator who will change the replacements, in the end you will have one Localizable.strings file per language.

# in your project directory
genstrings Classes/*.m

The strings file will look like this:

/* Player View */
"Player 1" = "Spieler 1";
 
/* Player View */
"Player 2" = "Spieler 2";
 
/* Player View */
"Player 3" = "Spieler 3";

All strings on the right side of an equal sign should be changed. If you want to you can aggregate multiple tokens under one header if it is identical, but don’t change the comments because otherwise the NSLocalizedString will not work.

Step 3: Add strings file to your project and make it localizable

Now go into your project and add an empty Localizable.strings file. Right-click on it, Get info, and add localizations for all languages that you want to support. Note that Xcode adds “English” for you, but this is outdated: your locales should use the 2 letter ISO code. en for English, de for German and so on. So after adding those you should remove English. 

This will create localization directories under your project directory that are called like de.lproj. Those directories contain all the language-specific variantes of all files for this language. You can even localize images like that.

Step 4: Troubleshoot

That’s all it takes…  if you haven’t accidentially change the strings files away from UTF-16. Because then you will find that the build process for your app works fine, but on the device localization will fail to work. If you look into the app bundle your strings file will have been renamed like Localizable.strings.45601. Of course the iphone will be unable to find this file like this and instead use the default localization.

In the info of any strings file you can change and/or force it to UTF-16. Save yourself the pain of not knowing why your pretty new language does not show up. Another reason I found (which nobody warns you about) is those darn semicolons. Should you loose a single one you again don’t see your localization even though the building works fine. Been there, trust me.

In Summary, localization is really simple with Objective C. Seasoned programmers would probably even do step 1 while they are coding. It’s just a little bit more code, but the rest is almost automatic. Now all I need to do is find people who speak French, Spanish and Japanese.

Story of my Life

The company that puts the bread on my table asked me to write an article about how my programming hobby led to an idea that eventually got submitted for patenting to the US patent office. People liked it very much so even further departments asked me if they can use the article.

Now that it has gone public inside the company I sought and got permission to put it here for you to enjoy. Note from editor: Minor changes where made to protect proprietary company information.

 

How My Hobby Led to an Invention that Amdocs Patented

Oliver DrobnikWhen I was just a young boy I loved to play with LEGO pieces and was fascinated with how you can put different things together to form something completely new. The concept of “the whole being more than the sum of its parts” fascinated me and my passion for constructing things carried over to writing computer programs on the MSX home computer that my father bought in 1983, when I was 9 years old.

Previously our family had moved to the suburbs of Vienna, the Austrian capital, and being away from the many distractions of the big city left me lots of time to sit in our basement and experiment with simple computer programs which I would create first in Microsoft BASIC and later in Turbo Pascal.

A neighbor of ours was the kind of person you nowadays would consider a “nerd,” but he was the first person with whom I was able to share my passion for computers. So, I followed his example after graduating from high school and attended college for two years to learn how to become an IT engineer. Three years later the Austrian Chamber of Commerce awarded me with my Engineers degree.

At first I tried to go to university and even passed a couple of IT and English exams quite successfully, but after two years I got bored. An academic IT career, in my opinion, was miles away from the hands on work of creating real programs that solve real problems.

So right after compulsory military service, I applied for a job at TelCo and got on board with them as an IT operations guy. I would perform billing and rating runs, while constantly creating scripts to make such mundane tasks more automatic and self-correcting. Unix shell scripts were my “weapon of choice” for saving myself a lot of time.

My next ‘idol’ was a guy who showed me how to write C++ programs that I could debug on Windows and later compile for HP Unix or Tru64 without changing the code.

A couple of companies later, I became one of the first owners of the iPhone in Austria. Even long before it was officially available, I knew I just had to have one. Since the inception of GSM I had been a true follower of Nokia, but the promise of a touch-based full-screen interface together with ‘always-on’ internet connectivity seemed to me to be the holy grail of mobile computing. Right after the official release of the iPhone 3G, I upgraded and as soon the Software Development Kit was available I signed up for a developer’s account. At first learning this strange new flavor of Objective C was daunting, but endless hours of experimenting and puzzle solving got me to a skill level that now allows me to create applications within a couple of weeks in my spare time.

I found that the more code I wrote while travelling on the train, the more ideas I got for new iPhone programs that would be incredibly useful to me. One of those ideas was clearly on too large a scale for me to implement by myself, but when I heard about the Amdocs innovation intranet site I decided to submit my suggestion to Amdocs.

[Proprietary details of patent omitted here]

Several months passed before I received a reply from Amdocs’ innovation team, but when the reply came things moved quickly. I was informed that my idea was worthy of patenting because of its proximity to Amdocs’ core business – mobile commerce. Based on what I had previously written, a patent lawyer created a 20-page document which detailed my invention in minute detail. When I approved the draft it was submitted to the US patent office.

Many people have since asked me why I would give my idea to Amdocs in exchange for a one-time premium. My response to them is that I would rather see a good idea be owned by a great company that has the potential of implementing it than keep it to myself in the hopes that a profit may be gained from it someday.

Today, at age 34, writing my own programs feels like ‘LEGO for Adults,’ a sort of creative self-expression. My full time job at Amdocs is to take care of IT at the Amdocs Interactive Vienna site; sitting down in my spare time and creating solutions for my own computer problems makes me feel like I can create something lasting that also has value to others and, at the same time, balances and energizes my creative juices.

From my experiences, I can only recommend to all readers to take on a hobby where they create something with their hands – be it carpentry, knitting, programming or anything else. I have found that such a constructive hobby increases your general satisfaction level and makes it much easier to deal with the stress you might face at work.

GeoCorder Breaks Records

After the first full day of sales I am really happy with how GeoCorder is doing. I did not expect such a huge number of downloads. But hey, it’s useful and it’s free… for a limited time.

USA leads the way with 240 downloads, Germany with 43 is not-so-close second and Canada on third place is barely more than the average with 29. In total the top three countries amount to more than half of downloads, 495 in total.

Actually those numbers might give a very good indication about what international distribution of downloads to expect and which languages to target. With English you will cover more than two thirds of market volume and German approx. 10%.

GeoCorder is useful to anybody in any language and therefore I think download number are not skewed by people not downloading because they don’t understand the app’s core content. GeoCorder has so little text that your language does not matter.

I will raise the price to $2 once I reach 1000 downloads. While this will definitely reduce downloads dramatically I hope to turn a couple of bucks for the effort I put into this app.

UPDATE: I dropped the price again to $1 because daily downloads dropped from over 200 to zero.

New App available: GeoCorder

Our latest addition to the app store is GeoCorder, currently free. With it you can simply record GPS tracks in full detail and later e-mail them to contacts in your address book.

Actually I just put it online to get some report data about free apps, so that’s your advantage. You can get it now for free. Later I’ll probably raise the price to 1 Dollar, because I believe that if people like the concept they will also buy it for that amount and the earnings can then go towards further development of the app.

GeoCorder had been rejected by Apple several times. First due to my abusing a standard button for something completely different. Later I had a nasty crashing bug that you would only see if you tried to e-mail a track without network connectivity. Apple thought that this might “confuse users”. And frankly so did I after being able to duplicate the problem on iPhone Simulator.

I persisted, improving my code several times and resubmitting as often. Finally, today, I got the infamous “Your application is Ready for Sale” e-mail. Hooray!

More info here.

String to Array of Characters

Michael asks:

“How do I split the characters in an NSString into an NSArray? In BASIC I could split a string with empty character, but in Object C this does not work”

One way to do it is to just get one character substrings:

NSString *s = @"Hello World";
int i;
NSMutableArray *m = [[NSMutableArray alloc] init];
 
for (i=0;i&lt; [s length]; i++)
{
	[m addObject:[s substringWithRange:NSMakeRange(i, 1)]];
}
 
NSLog([m description]);  // most NS* objects have a useful description
 
[m release];  // don&#039;t forget

In Objective C every time you need a starting point and then a length from there it’s call an NSRange. Conveniently there is also a *Make macro for most structures like this. So in this case you just NSMakeRange(position,length) and pass this to a substringWithRange.