iOS UDID Officially Gone

Today Apple announced that as of May 1st 2013 iOS applications retrieving UDIDs (Unique Device IDs) will be rejected prior to distribution.

Previously the UIDevice class’ uniqueIdentifier method used to obtain the UDID had only been deprecated.

Apple now recommends developers to use the UIDevice class’ identifierForVendor method which is available from iOS 6.0.

The returned value is unique to the device for all apps developed by a given software vendor.

This should replace the UDID quite well, while also preventing multiple vendors from working together to track users across apps.

If your app must remain compatible with iOS 5 or earlier this method will not be available and the workaround, as I wrote in an earlier post, is to access the device’s network MAC address which is also unique to the device.

How to Make a Build Script for Bundling dylib Files into an OS X App

Although I’m not new to Mac development I never needed to embed a dynamic library (.dylib file) into an app until just recently. Needless to say I struggled with this for a while.
I also wasn’t able to locate any single, up to date and reputable tutorial on the topic so I wanted to collect the bits and pieces I found into this single, easy to understand article so I could help others.

The strategy that worked for me, and which I’ll cover below, was to create a post-build script.

 

Understanding Dynamic Libraries

Dynamic libraries have encoded within them a “name” which actually serves as a path by which they are located. In order for your already built app to reference a dynamic library it needs to know where to find it and during the build process this encoded name becomes that location. When your app starts up it attempts to load all its dependencies at their named paths and unless said dynamic library is installed on the user’s Mac the app will crash.

Of course you can never be 100% certain a user will have the dynamic library beforehand and installed at just the right location. It therefore makes more sense to bundle the library within your app.

Determining the Encoded Name

In your app’s main target create a post-build script and enter the commands below, replacing MyProject with the name of your project and the “ThirdParty/Abc/” with the path of the .dylib file relative to your project’s base path.

echo OTOOL BEGIN\n
otool -L MyProject/ThirdParty/Abc/Abc.dylib\n
echo OTOOL END

Now build your project and inspect the build log (Right click on any build issue and select the “Reveal in Log” menu item). The output from otool’s execution should appear below “OTOOL BEGIN” and above “OTOOL END”.

Did it work? Okay, now remove these 3 lines from your build script but keep the dynamic library’s name handy for the last step.

 

Overriding the Dynamic Library’s Encoded Name

The below command overrides the dynamic library’s name to one that will match its path within your app’s folder (remember an OS X app is really an .app folder, not a file). Typically libraries appear under the “Frameworks” folder. NOTE: Make sure you have a “Copy Files” build phase which will copy the file from its source to this “Frameworks” folder by specifying it as the Destination.

install_name_tool -id @executable_path/../Frameworks/Abc.dylib MyProject/ThirdParty/Abc/Abc.dylib

NOTE: The executable path variable is the folder in which the true binary executable is located. Typically in the PRODUCT_NAME/Contents/MacOS folder. So the path @executable_path/../Frameworks/ is really PRODUCT_NAME.app/Contents/Frameworks.

In the above command replace Abc.dylib with your dynamic library’s filename.
Also replace MyProject with the name of your project.
In this example the source folder for the Abc.dylib is under “ThirdParty/Abc” and you’ll need to provide an appropriate path to your dylib file, following your project’s conventions.

Now that the dynamic library’s new encoded name matches the new location in the “Frameworks” folder the next step is to tell your app to expect it there.

Remember this is a post build step. Your app was originally linked against the not yet renamed dynamic library and thinks it needs to look for it there still. This is where we use the encoded name otool reported to us…

 

Modifying the App’s Reference to the Dynamic Library

The below command changes the referred location of the dynamic library from its original path to its new one within the “Frameworks” folder.  Add it to the bottom of your script.

install_name_tool -change usr/lib/Abc.dylib @executable_path/../Frameworks/Abc.dylib MyProject/ThirdParty/Abc/Abc.dylib

As with the previous step, modify the paths to reflect those of your project and take special careful to type all paths accurately.  The above “usr/lib” path may differ from that of your dylib and this is why you used otool.  Take the path you discovered with that tool and if different from “usr/lib” replace it.

Run the build once more. If no errors occur and the dylib file is indeed bundled in the “Frameworks” folder (you can check by stripping the .app extension from the folder) there is one last thing to test. Running the app!


Run It

Archive the project (Xcode menu Product → Archive). This will open the Organizer window. From here you can press the Distribute button which will prompt you for the method of distribution.
Choose “Export as Application” and export it to your desktop. The app is now waiting for you there and you only need to double click it. If the app starts up without complaint you’ve successfully bundled the dynamic library!

If the app doesn’t execute, check the error report OS X offers in the crash dialog to see what file path it expected to use. You can also reuse the otool command shown in the first step at the bottom of your build script to double check that the dynamic library was indeed renamed.  Most errors end up being mistyped paths and otool can prove so.

How to Draw Thick 2D Lines in OpenGL

Background

In a recent project I needed to draw lines NSBezierPath style in OpenGL at different widths and which may be zoomed arbitrarily by the user. As OpenGL does not natively support rounded end caps on lines drawn with glVertex2f I employed the popular technique of drawing a GL_POINT at both ends of a line using the same vertices as the line itself.

This approach works to a point but, I discovered, not on every machine.

On my development iMac the OpenGL implementation handled this without complaint, but when testing it on a different machine (a Mac mini) I noticed an odd effect.  The points I was using to simulate end caps were gigantic in comparison to the lines themselves and it resembled a narrow bone with large joints on both ends.

I investigated the cause by querying the GL_SMOOTH_LINE_WIDTH_RANGE and GL_SMOOTH_POINT_SIZE_RANGE parameter values.  It seems the points could range from 1.0 – 64.0 in size but the lines only 1.0 – 7.0 on this test machine. What I needed was a width of 20.0.

At this point it was clear that I would need to replace my native OpenGL lines with quad based pseudo-lines. Searching online for an existing implementation yielded nothing that fit my needs so I ended up rolling my own, which I now offer for free to everybody via
my Github repository (JFOpenGLUtil.h, JFOpenGLUtil.m).


How It Works

The implementation is simple. A GL_QUAD is a 4-sided polygon consisting of 4 vertices and, if kept narrow, it can be made to resemble a line.  If we know the line width and the beginning and end points we’re ready. We only need to calculate one offset point from the beginning point.  This offset point is ½ the line width in a perpendicular angle from the line. Once this offset is determined it can be reused 4 times to render the quad.


How To Use It

Unlike glVertex2f, you’ll need to provide 2 points at once. This is because the angle (from the beginning to the end) is needed to determine the GL_QUAD vertices.  If you’re using a for loop to iterate over your points just skip the first point like this…

Point *previousPoint = nil;
for (Point *point in pointArray) {

	CGPoint endRenderPoint = [point renderPoint];

	if (previousPoint != nil) {
		CGPoint beginRenderPoint = [previousPoint renderPoint];

		[JFOpenGLUtil drawQuadLineWithWidth: lineWidth
					red: redColor
					green: greenColor
					blue: blueColor
					from: beginRenderPoint
					to: endRenderPoint];
	}
	previousPoint = point;
}

 

With this approach lines of virtually any width can be supported.  But don’t forget, anti-aliasing is needed to keep the quad line from looking jagged of course.  I hope this implementation helps you if OpenGL has been less than cooperative with your app’s line drawing.

I made this! – Rethinking Code Ownership

Except in more complex arrangements involving freelancers/contract workers the code that developers create obviously belongs to the company. When we talk about code ownership, we imply the knowledge to maintain that code. Code can be a very brittle thing and the developer who created it may be the only person who understands the background of why the code is the way it is.

I’ve been fortunate to have worked in many projects which took various approaches to code ownership. In fact I’ve witnessed both extremes of the spectrum and various points in between.  The result?  I’ve come to the inescapable conclusion that code ownership is not only a good thing overall, but that it really ought be an unalienable right for developers. I’m ordinarily open-minded and don’t consider myself a purist on most matters but this might be an exception.

The reason is simple, as in all things, when a person’s name is attached to a thing, more care will put into its making.

I’ve been developing professionally now for over 15 years. Long before movements like XP, TDD and Agile Development became buzzwords. These movements have introduced or at least popularized certain beneficial techniques but also some just plain bad ideas like backseat driving as a productivity booster and the topic of this post, code ownership or rather the movement against it.  As I said above I’m generally not a purist so I prefer to handle each problem on a case-by-case basis and not handcuff myself with artificial restrictions.

I could write a treatise about how religiousness has taken over the profession of development but in this article I just want to discuss the rationale behind the movement against code ownership and why I think it misses the point.  So without further ado…

 

Flight Risk

The biggest crux of code ownership is that of flight risk. What if the owner of a piece of code quits and takes his knowledge with him? What if he uses his knowledge as leverage to force a raise/promotion out of us? What if this? What if that?

There are a lot of “what if?”s out there. These scenarios could indeed play out but one thing that isn’t a “what if?” is that less experienced developers are going to make bugs and continue making bugs until they learn to fix them.

To counteract this possibility of flight risk it was proposed that total ownership of code be abolished. In its place various approaches have arisen…

  • 100% shared code. Everybody owns everything/nothing. If a bug is found anybody can fix it. If the code isn’t clean, anybody can go in and clean it etc… If anybody quits it shouldn’t impact the company at all, in theory. The only problem with this approach is it doesn’t work. Development is a complex endeavor and it’s simply not possible to know everything. It’s more often the care that developers will be fixing code they’ve never seen before let alone understand.
  • Team ownership. A project’s code is owned by the team. If one member of the team quits the others will pull up the slack. While not perfect, this is a more balanced approach. You don’t need to know everything, just the projects in which you participated.

It’s true that developers can pull up stakes and leave a company at any time (they aren’t slaves). If he or she has all the knowledge about a piece of code then that will be lost. Or will it?

Assuming the code remains, if a compiler can understand it well enough to carry out the instructions within, certainly a developer with some time can do the same. I’ve often inherited such code with little to no documentation. It takes time but not all is lost.

 

Going in Reverse – Keeping Developers Around

All other factors being equal, the more a person’s name appears in code the less likely that person is to leave the company. Developers are builders and we take pride in the things we build. We indeed become attached to them. That attachment, if it’s a positive one at least, can very well anchor us down.  And it’s a compounding effect!

80% of development is maintaining and extending existing code and the longer we work on a thing and the more code we own the harder it is to just leave it.  By abolishing code ownership you are actually discouraging this attachment from occurring and are arguably giving little reason for developers to stick around.

You might ask “Isn’t the attachment to the delivered project enough?”  The answer is “no”.  Attachments aren’t binary, they’re degrees, and the more there are the better.

If you fear that your developers will up and leave at any time you may have the much larger problem of a crummy work environment. Any great place to work will inspire people to stay.

 

Code Ownership = Code Accountability

One of the best properties of code ownership is that it allows the code to develop the developer. If I make a mistake (it’s been known to happen) and I’m tasked with fixing it myself I’m going to learn from the mistake and the likelihood of me repeating it in the future will diminish if not drop to zero entirely.

Remember, bugs don’t come into existence by themselves, it is we developers who put them there in the first place. Of course we didn’t do it on purpose, they were put there as a consequence of us not understanding all the circumstances in which our code would run. It follows that one of the best ways to prevent bugs is to learn from them by fixing them.

The alternative, is to have somebody else fix my mistakes in which case I might not learn much if anything. Adding insult to injury, the developer who fixes my bug has now been burdened by me and with each such occurrence will hold me in lower and lower esteem.

Personally, I would prefer to be held accountable for my successes and my mistakes.


Fixing other People’s Code

Ask any developer if he or she enjoys fixing other people’s code. I guarantee you that you’ll be given a resounding “NO”. I hate it too. It often feels unfair.

Over my many years of developing I’ve settled upon patterns that have proven themselves capable of creating robust code. I’m not perfect of course but I don’t leave a mess everywhere for others to clean up. I see it as an issue of discipline and I just don’t want to be a dick to others.

The fact is, in the same sense that you can’t become a programmer simply by reading a programming book you can’t learn from your mistakes if somebody fixes them for you and just tells you where you went wrong. You have to fix bugs to learn how to prevent them. I’ve fixed my share and I’m a better developer because of it.

 

Personal Projects

My most robust code is in my personal projects. If it wasn’t I’d be inundated with bug reports, which I cannot afford to deal with. I own not only the knowledge but the very code itself. Unless I open source all my code nobody will fix my mistakes for me so that code has to be the best.

Each developer is of course different but I suspect those who truly care about quality share the same experiences as myself in this regard.

Even developers who work in an environment where code ownership is considered evil should at least pursue their own personal projects during their off time and see for themselves which approach works best.

It might just give us cause to rethink code ownership.

Open Source Code for Developing Sudoku for iPhone and OS X

My Love For Sudoku

I’m a Sudoku addict. My love for Sudoku has most certainly surpassed unhealthy levels.  Naturally, being a developer I at one point had an interest in developing my own Sudoku game just to see what it entails.

The concept of Sudoku is a very ancient one but the game’s recent popularity is attributed to the company NIKOLI which owns the copyright to the word “Sudoku” and the Japanese “数独” (lit: “number alone”) within Japan.

Game concepts, in and of themselves, cannot be copyrighted and to get around any copyright disputes I considered calling my game “9 Lives” and varying the gameplay enough by employing a strict rule of allowing up to 9 mistakes before the player loses. Essentially I was interested in making a Sudoku game for pros (no hints or anything).

However, I later learned through the Quinn/Tetris debacle that even though game concepts cannot be copyrighted, the owners of such games still show their legal fangs if you encroach on their territory and considering that my presence here in Japan is also where NIKOLI is based I reconsidered developing my own game.

That was 5 years ago, when I was just beginning to learn Objective C and OS X development. While cleaning some things up on my Mac I recently rediscovered my old Sudoku board generator code and it feels like a waste to just let it sit there unused.  Instead I decided to rewrite it (the original code is too embarassing to release) and donate it to others interested in making their own Sudoku games.

Most of the Sudoku games I have played only offer pregenerated boards. My code creates random ones each and every time. As such it depends on a random number generator, in this case my JFRandom utility class which is available on github.

If you are outside Japan and interested in making your own sudoku game check out my Sudoku Board Generator.

 

How To Use It

This one line is all you need to generate a random Sudoku board…

SudokuBoard *board = [SudokuBoardGenerator generate];

Outputting the resulting Sudoku board with NSLog will show the board in full like below…

7 1 8 5 4 3 6 2 9 

6 9 3 2 1 8 5 7 4 

5 4 2 9 7 6 3 1 8 

4 3 5 7 9 1 8 6 2 

1 6 7 8 3 2 4 9 5 

2 8 9 4 6 5 7 3 1 

8 5 1 6 2 7 9 4 3 

9 2 6 3 5 4 1 8 7 

3 7 4 1 8 9 2 5 6 


The board exposes 9 sectors (3×3 grid) and the numbers contained within for you to display in your game.  That’s all there is to it.  The details are all abstracted away from you.  If you have any questions please don’t hesitate to contact me at jay [AT] this domain.

Matt Gemmell’s 25 Rules for API Design

Yesterday Matt Gemmell posted a very comprehensive blog post regarding API design that I just had to share here. Although the post leans towards UI control design in Objective C these rules still apply to other varieties of APIs (Web, REST, etc…) and platforms.

http://mattgemmell.com/2012/05/24/api-design/

If you’re developing an API or component that might ever be consumed by other developers you would do well to bookmark this page as I just did!

The Case Against Universal Apps

Since the advent of the iPad, iOS developers have had the option of distributing universal apps that bundle logic intended for both iPhones and iPads.  But is this a good thing?  In this post I quickly discuss the outcomes of going universal and why they might or might not make sense for your app.

Pricing

You’ve worked hard on your app and you’d like to be fairly compensated for that hard work.

It’s a known fact that iPad users are willing to pay more than their iPhone counterparts for apps specifically designed for the iPad.  If you go universal you’ll have to choose a single, shared price for both devices.  Considering the amount of effort you put in to making the iPad edition of your app that much better for the iPad don’t you think you deserve better?

To add insult to injury, an iPhone user who purchases your universal app will get it for free on the iPad and benefit from the iPad specific parts.  So instead of earning 99 cents for the iPhone edition and $1.99 for the iPad edition you’ll only receive 99 cents, period.

Pricing Experimentation and Sales

You’ll likely be experimenting with your price to find the sweet spot which generates the most revenue.  This number can never be the same on both device classes due to different demographics.

And what if you want to offer a sale for just one device class to bring more awareness to it?  You probably have already guessed that with a universal app you will have to offer your sale price to users of both device classes.

App Size

Why should an iPhone user consume storage space for the iPad specific logic or vice versa?  Storage is at a premium on these devices already.

Code Complexity

Your iPad logic will be living side by side in the same project as that of your iPhone.  You’ll need to detect the device class at runtime and switch between logic.

Staged development

If your time or development budget are strained, you can focus on one device class first by not going universal.  You can determine your app’s popularity after release and decide later on if the other device class warrants a port.

App Store Ranking

Apple views universal apps as being distinct on the iPad and iPhone.  They may share common logic but from the user’s perspective the UIs are very different and any reviews they give reflect the experience of only one of them.  Accordingly Apple separates the ratings and reviews of both device classes in the App Store.  Ratings are believed to major play a factor in App Store ranking so having them separated eliminates any potential for boosting your rank by opting for a Universal app.

When do Universal Apps make sense?

If you have a very simple app that you know will always be free the demerits I mentioned above don’t warrant as much concern.

One merit of going universal is that you can call your app by a single name without appending “for iPad” or “HD” to the title and this can make your app easier to find in the App Store.

Conclusion

In many cases Universal Apps don’t work in favor of developers and in some cases they don’t make much sense for users either.  Careful weighing of concerns should take place first before you commit to either approach.

One thing is certain however, divorcing an already universal app into two entails a lot of effort and can cause confusion for users.  Marrying two editions into a universal app, should circumstances later warrant it, is much simpler.  Just like marriage, you should stay single until you’re absolutely sure you’re ready for it.

Agent App – How To Prevent A Mac App Icon From Appearing On The Dock

If you’re developing a Mac app that runs in the background most of the time (in other words an agent app) you typically don’t want to your app’s icon to consume space on the dock.

Luckily for us, achieving this is quite trivial.  Just follow these steps…

  1. In Xcode (4.3 for the purposes of this post), open the Project Navigator (the folder tab under the Run button).
  2. Select your project (the parent item with the blue print icon).  This opens the project details view.
  3. In the project details view select your app’s main target (under TARGETS) and select the Info tab.
  4. If it is not already present add an ‘Application is agent (UIElement)’ item under Custom Mac OS X Application Target Properties.
  5. Set this item’s value to YES (as shown below).

Application Is Agent

 

And you’re done!

From now on, whenever you execute your app its icon will no longer appear on the Dock or in the Alt + Tab application switcher.

Update to JFUrlUtil

Just a small update regarding the JFUrlUtil URL encoder for Objective C I introduced recently:  I recently encountered URLs in the wild containing the “@” character and accordingly have now added support for encoding it.

The updated JFUrlUtil is available here.

If you find any URLs that don’t encode properly please inform me (jay@jayfuerstenberg.com) and I’ll improve this utility.

Singleton Pattern in Objective C

Objective C unfortunately does not support the singleton pattern with which Java developers are familiar.  The reason stems from the fact that there are no private methods in Objective C to prevent the init constructor method from being invoked.

There is however a simple hack that can provide the benefits of the singleton pattern in Objective C.  Singleton classes tend to be managers of data so let’s go with the class name JFDataManager in this example.

 

Going Global

Since Objective C doesn’t support the singleton pattern we have to resort to using a global variable the way C does.

A global variable can be referenced from anywhere in the application and since we want to discourage the direct manipulation of this global variable we need to hide it.  The best place to hide it is the one place nobody will look, in definition file for that class, JFDataManager.m.

Doing so will render it unlikely of being referenced since we’re never supposed to #import or #include *.m files.  Note that I said unlikely.  Technically any inquisitive developer can find and reference it.

The declaration might look something like this…

JFDataManager *__JFDataManager_sharedInstance__ = nil;

Since the class name was previously declared in JFDataManager.h this instance is completely legal.  Now since we’re dealing with global variables there is the risk of name collision so it’s good practice to go with an instance name that includes the class name as I’ve done above.

 

Exposing It The Recommended Way

The convention for telling developers that they should access the singleton instance and not instantiate their own is to offer a class method beginning with the word “shared”.  You may have noticed a sharedApplication method exposed by the NSApplication and UIApplication classes.  You’ll want to follow this approach.

JFDataManager will expose the below method…

+ (JFDataManager *) sharedManager;

The implementation of this method will return the shared instance and if necessary lazy-initialize it like this…

+ (JFDataManager *) sharedManager {
	if (__JFDataManager_sharedInstance__ == nil) {
		__JFDataManager_sharedInstance__ = [[JFDataManager alloc] init];
	}
	return __JFDataManager_sharedInstance__;
}

 

Getting the shared instance is as simple as invoking the sharedManager method.  That’s really all there is to using the singleton pattern in Objective C.  It involves a bit of trickery but Objective C developers using your class will enjoy the same benefits Java developers already do.

 

URL Encoding In Objective C

A new project I’m working on uses NSURL to a great extent and as useful as that class is, it has one sour point.  It is unforgiving of improperly encoded strings when instantiating an NSURL via the URLWithString convenience constructor.

Naturally I needed an encoder that would escape the unsafe characters.  There is a decent selection online but they each lacked a few properties I need in my project.

  1. The domain + path portion needs to be left alone (no conversion of slashes etc…).
  2. Any previously escaped sequences (like %20) need to be left alone and not have the percent escaped again (like %2520).

I’ve given up looking for an existing solution and rolled my own, which I now share with you!

My implementation is called JFUrlUtil (Download) and is open sourced under the Apache license.

 

How it works

JFUrlUtil features a single public method called encodeUrl.  The approach it takes is basically to iterate over each character in the URL string you provide and escape where appropriate.  It is quite performant as it avoids invoking NSString’s stringByReplacingOccurrencesOfString:withString: method for each unsafe character.

Compatibility

I’ve tested this implementation against tens of thousands of URLs and have yet to find any encoded URL capable of causing NSURL to frown.  If you can find one I didn’t anticipate please let me know (jay@jayfuerstenberg.com) and I’ll gladly update my implementation.

 

When To Localize Your iPhone App

In the past I’ve highlighted some software design mistakes made by others and today I want to point the finger at myself and talk about a mistake I made in localizing my first iPhone app KEYBOX.  It is my hope that this article will help others avoid the same pitfalls I experienced.

I made KEYBOX to scratch a personal itch (digital privacy) and it was first released in 2005 as a J2ME app for Vodafone Japan’s market.As such it was never localized to any language other than Japanese.  That all changed this year when I ported it to iOS.  As the App Store is an international affair I naturally would like KEYBOX to benefit as many people as possible.  The main barrier to this is of course language.

For release 1 I decided to localize KEYBOX into the 4 languages with which I am familiar: English, French, Japanese and Spanish.

I now believe doing so this early was a huge mistake as it introduced two issues for me: serious delays in releasing and numerous layout changes.

In localizing KEYBOX, I underestimated the amount of effort it would take.  There are over 200 text snippets to translate from English.  Using online dictionaries to double check my translations and searching for real-world instances of each text snippet before issuing the final check of approval for each takes upwards of 20 hours for each language.  When you’re only doing this on the side (weekends and some evenings) you can imagine how much it delays the release of your app, which is otherwise feature complete and ready to go.

And then there was the EULA!  KEYBOX is unique in that it requires a rather lengthy EULA to protect myself from users who might be less than responsible with it.  Writing the English version was an experience in its own right.  Writing it in Japanese would’ve been a train wreck without help from my loving wife (also Japanese) and it took over 30 hours spread across 3 weeks.

An interesting surprise was that people in countries whose languages are not yet supported still download KEYBOX, and in droves!  Italy is perhaps one of the biggest markets and KEYBOX isn’t even in Italian yet.  I don’t know if support for Spanish and French is what makes this possible (the Romance language connection?) as KEYBOX is about privacy and I don’t solicit my users for such information.  While I’m sure that supporting multiple languages opens the door to more people enjoying my app, having so many languages supported from version 1 is perhaps less important than I initially thought.

Now to the issue of layout…  In comparison to English, Japanese can be an extremely compact language and most words are only 2 kanji characters in length.  And then there are French and Spanish whose words tend to be much longer than their English counterparts.  This naturally leads to sentences that expand and contract in length depending on the user’s locale.

Before translating the text I was tempted to avail myself of iOS’ ability to auto-adjust font sizes for a statically sized label.  I figured the sentence length wouldn’t vary so much and that an auto-adjusting font size could handle that difference.

I could not have been more wrong.  Doing so resulted in text so small that it was rendered unreadable and sometimes text that gets clipped off by the boundaries of the container label.

I learned, the hard way, that multilingual apps really are better off having multi-line labels with a fixed font size.

Going multiline however introduces a new problem.   It forces us to position controls spaced far enough from each other to compensate for the wordiest languages whose labels will expand the most.  Release 1 of KEYBOX followed this approach.  It worked for the most part but it took a lot of manual testing across languages to make sure the text was readable and that there were no overlapping elements.

I knew that I couldn’t take this approach forever and for release 2 I decided to change tactics.  I developed a high-level, goal-oriented layout utility class that lets me accomplish tasks such as “place this text field 10px under that label and center it on screen” or “left align this label 5px to the right of that icon”.  As a result I no longer have to consider the pixel locations of positionable elements across all locales at once and can focus more on the general look of the UI.  In some ways it works like HTML with the end result being one element following the last but unlike HTML it still retains simple, pixel perfect control.

 

Example:

KEYBOX 1 Without Dynamic Layout

KEYBOX 1 – Without Dynamic Layout

 

Keybox 2 import export

KEYBOX 2 – With Dynamic Layout (Text is directly under cloud image and the warning icon is to the left of the text)

 

I intend to open source this layout engine for those who want to maintain a professional layout for their multilingual apps but my best piece of advice would be to release one language at a time and just get your app out there first!  From there on, you can roll out new languages gradually.