Make sure to test every code path

My story is not as dramatic as some, but I thought it was amusing. I’ve been using my PDF class for quite a while now, and it’s seemed to work perfectly so far in shipping code. But while working on a new app suddenly a tall skinny image was rendering too tall. When I fixed that, suddenly the image was off center. What was wrong?

Well, the PDFView class is designed to render it’s PDF’s as large as possible while preserving the aspect ratio. This means it takes the entire space in either the x- or y- direction, and centers itself in the other direction. But out of 913 images, apparently none of them filled the y-direction so that centering code had never been tested. So the code was failing to actually center properly.

It was a simple fix. Here’s the few lines to change to make the drawRect code work correctly:

        if (xRatio < yRatio) {
            ratio = xRatio;
            CGContextTranslateCTM(context, 0.0, -(self.bounds.size.height-boxRect.size.height*ratio)/2);
        } else {
            ratio = yRatio;
            CGContextTranslateCTM(context, (self.bounds.size.width-boxRect.size.width*ratio)/2, 0.0);

Leave a comment

Filed under Uncategorized

No one else to blame this time…

We recently added a great feature to ArtikPix. Now, after a student practices the word, they can press a small button and be prompted to use it in a sentence as well. This is a much requested feature that is also a best practice in speech therapy. I’m very proud we were able to get it in so quickly.

Along the way, I tried to fix a long standing bug. In order to hear each word clearly, the app is set up to queue them up, and only play a new word when the last one ended. The problem would come up if the user recorded the students voice before a word ended. This would interfere with the notification that the word ended, and cause the next word to never be played. In fact, this caused no more words to be played until the app was ended.

This was made much worse by 4.x. Now that the home button only caused the app to be suspended, the words would not play until the user double clicked and deleted the app from the recent app list. This was difficult enough that users assumed the app was broken, and to some it seemed only rebooting the device would restart the sound.

So I researched a bit. The easiest solution turned out to be to allow sound playback and recording at the same time. This is known as changing the Audio Session Category to allow Play and Record. It solved the problem, and appeared to work well. We tested it on 2 models of iPhone, the latest iPod Touch, and the iPad. It seemed a small enough change to put out with basic testing, after all the testing of the sentence update.

Any developers recognize the step we forgot? By default, changing the Audio Session to kAudioSessionCategory_PlayAndRecord forces the audio to be played through the ear piece on the iPhone. It’s bad enough that this made the sounds quieter, but on the 2nd generation iPod Touch, there’s no ear piece, so there’s no sound at all.

The worst part is we did not realize this when the update was approved by Apple. So it took a little bit to recognize the problem when the support emails started coming in. And I had to wait until my day job was over to do anything about it.

To make it even worse, I did not have a 2nd generation iPod Touch. Most of the time, my iPhone 3G without cellular service acted the same, so I never bothered to get one. So I had to build a new version, and wait for Eric to test it. It took a couple of builds to get it right. For some reason, my stubborn brain did not seem to like the idea that you had to set the Audio Session active before setting the new category and forcing the sound to play out the main speaker. But I finally got it right, and Eric reported the app was working again on the older iPod Touch.

To top it off, once I had it fixed, iTunes Connect was down, so I could not submit the new version. I kept trying until Apple finally put up the “Scheduled Maintenance” page. I had to go to sleep knowing the fix was even further delayed. Luckily, the next morning, I got the update submitted just in time. Apparently developers on the iPhone Dev Forums reported that the Application Loader stopped working again for much of the day.

Since this could affect many of our customers, I also had to request expedited review from Apple for the first time. I hope our users can forgive us while we wait for Apple to approve the update that will bring things back to normal. And I hope we’ve learned our lesson about testing all the different devices before submitting. Anyone have a 2nd generation iPod Touch for sale?


Filed under Uncategorized

A business model gone awry…

Instead of discussing another iOS SDK issue, I thought some people might find it interesting to hear about the business model of my main app, ArtikPix. Or, more honestly, how the plans for ArtikPix went wrong and we made it work anyway. It has certainly been a wild ride from my viewpoint, and has not gone as I expected. But we now find ourselves with an app that is selling well, and starting to show it’s potential.

The story starts when I had just finished my first app, and my friend and business partner, Eric Sailers, had an idea for a new app. He wanted to replace the articulation cards that take up space in every Speech Language Pathologists office with an app that would allow him to help children practice their speech with bright modern graphics, a game, and features that would help him collect the data he needed to make sure they were improving. He loved the idea of not having to carry around all the cards, and I loved the idea that we could undercut the price of all the decks of cards and still be a high priced app. We were able to license graphics from SymbolStix, which gave us a rich selection of high quality, kid friendly images in vector form.

We came up with the idea of using In App Purchase(IAP) to allow us to sell decks individually or in groups. This way parents who might only need a few decks could buy them, but professionals could buy the cards all at once. And this way, the basic app could be free, so if Eric showed the app at a conference, anyone who wanted to could try it right away. And by having a free app with IAP, I could just write one version of the app, without having to worry about a free vs. a paid version.

Unfortunately, when we were getting close to ready, someone else came out with an app to do basically the same thing. We felt our app was easier to use, and one of our beta testers described it as not just more kid-friendly, but more parent friendly as well. But there were somethings our app didn’t do as well, so we felt we had to finish ours completely before going on the market. And we felt that we should compete by being the premium app that does more and charges more.

Then, the iPad was announced. When I saw they were allowing apps to be released on launch day, we decided to go for it. It was a lot of hard work, and a big risk developing for a platform that did not exist yet. But we were correct that we had no competition on the iPad at launch time, so it gave us an edge. And the app worked well on the hardware except for one little problem.

I had coded IAP incorrectly. Since it did not work at all on the simulator, it was the most likely thing to go wrong. I suppose it was best it failed the way it did. But it meant everyone who downloaded the free app got all the content included. Our expensive app was being given away free. And when I tried to fix the IAP, there were bugs on the iPad that made it hard to test, let alone fix. I managed to get something together, but Apple was taking so long to even start reviewing the update. And we worried Apple would probably deny the update since we would be taking away functionality people had been using for a week already to make them pay for it.

So we had to change plans. First, we advertised ArtikPix with Moms With Apps for their free app Friday feature. Might as well take advantage of our mistake, and make it look like we made it free on purpose. And advertising a free app with Moms With Apps does lead to a lot of downloads, so the free publicity and attention worked out well. Then, once the Friday was over, we made it a full price, paid app. The idea was we would make it a full price version, to be followed up with a “freemium” version with IAP. I thought the IAP version would be so popular that we could eventually migrate the paid customers over to it. I even figured out how I would move their licenses over, so they would get everything in the free app automatically.

So we worked on getting the app ready to compete with the other apps already available on the iPhone/iPod Touch. We added more decks, and improved granularity for the data collection. In order to get the app on those devices quickly, we decided to implement them first on the iPhone, with a universal binary to follow.

While we were doing this, we got some interesting feedback. Apparently, iTunes will let you buy the iPad version even if you don’t have an iPad. So we had customers who had accidentally bought the app to use on an iPod Touch, where it didn’t work. All we could do was promise we were working on it. I had to take a full time job to pay the bills, and it was a lot of work to make the app we had in mind, so it took longer than I like.

Finally, we finished the iPhone/iPod version. It was the free version with IAP, so users could buy only the decks they needed. They were also given a discount if they bought them all at once. It was moderately successful, but it seems many people were not comfortable with IAP, and after trying the free version, they would then buy the paid version.

This was getting confusing, as the paid version did not yet work on the iPhone/iPod Touch. And the paid version did not have all the decks or features of the free version. So we placed a notice in the app description to explain how the next version would have all these features, and we would be raising the price when the update came out. But the fact that the free version had more features was starting to get us bad reviews. And we had more support emails promising the app would eventually work on all devices.

Luckily, it was easier than I hoped to merge the 2 versions into one universal binary. Since the IAP was the last part to be fixed, we released the paid version as a universal binary first. This turned out to significantly help, as now anyone who tried the free version got even more when they changed to the paid version. And those who bought the paid version for their iPod Touch could finally use it.

Then, shortly after, the free version of the universal binary got approved. Guess what, it was delayed because of a build problem with IAP. While enough people bought individual decks that I’m glad I made it that way, more people who tried the free app turned around and bought the paid version. This was the final nail in the coffin for ever having just 1 app. So this post, part of the iDevBlogADay series, turned me onto Hudson, which allows me to automate builds. When I check a change into Subversion a change, Hudson builds both version at the same time so I will never again be confused which one is which.

When the paid version was only on the iPad, we would occasionally make the Top 10 Grossing Education Apps list. This is not a very visible list, so I doubt it drove many sales. But when the paid app went to universal, the sales were divided between iPhone/iPod and iPad, so we fell lower on the list. One of the little downsides. Also, the free app seemed to attract more bad reviews, so it’s lower rated despite the fact that it’s exactly the same app otherwise.

So today, we came full circle. Since ArtikPix is already free, we participated in another Free App Friday, and we lowered the price to purchase All Decks in the free app to 60% off. It caused a few problems. One mother had been buying decks a few at a time when she could afford them, and the app won’t let you buy all Decks when you’ve bought a few. So we had to give her the full version for free, since she had already paid enough. We had a few people confused when the Full version was not on sale. So we had to explain to them how the In App Purchase worked. And apparently Apple changes prices on Eastern time, so the sale ended too soon, and we had to manually extend it.

But sales were so good that both versions, free and paid, made the Top Grossing Education Apps charts on both devices. The free version broke into the top 10 on the iPad, and the paid version broke into the top 10 on the iPhone list. Interestingly, as we rose in the charts, so did the app that beat us to market. I’m not sure if we drove visibility of all the Speech apps, or if something else was driving them up at the same time. I spent too much of the day watching iTunes, as our competition stayed just ahead of us. But at some point in the evening, we passed them and stayed ahead. As I’m writing this, we are #6, and they are #11.

I have no idea what the future holds. At this point, I don’t even know how many sales or how much money this chart placement means. But I took my family out to dinner to celebrate, and I’m going to try to sleep after an exciting day.


Filed under Uncategorized

If I could save time in a database…

OK, it’s an awful song reference, but it was storing time has always been tricky. Since this was my first time handling time in an iOS project, I thought I’d discuss how I decided which method to use. This might seem straight forward to many of you, but it was new to me.

The first option is to use the DATETIME timestamp in SQLite. This has the immediate advantage that it does not even require code if you make it a required field. And it’s easy to sort on as part of your SELECT statement. This was my first choice, and I used it in the first version of my current app.

Be aware, the time is essentially what I know as universal time. It’s the actually the time from noon in Greenwich on November 24, 4714 B.C according to the SQLIte website. This is great if you want to remember exactly when something happened and it will change when the phone changes time zone.

This turned out to be an issue for me. My app stores when a student practices a task. And even when the phone changes time zones, my app needs to remember that the student practiced at 2pm last Thursday. So I had to change it.

NSDate is the next logical choice. If I was using CoreData, it looks like a logical choice. But it is not directly supported in SQLite, so I would have to convert it, probably into a double with the number of seconds since the reference date. It would work, but it would have the same problem with changing time zones.

So I finally ended up with storing time as a string, formatted in the default of the locale. This has a huge advantage for display, obviously. And I don’t have to worry about the display time changing when the phone moves time zone. NSDateFormatter makes it very easy to take the current time and convert it into a display string. And by using one of the built in formats, I used NSDateFormatterShortStyle, phones set to anything other than US English will automatically print date and time in the appropriate local format. I don’t even have to think about where the month goes in European dates.

Of course, this is not perfect, because now the sorting problem is much worse. If your phone is set for 12 hour time format, am/pm times do not sort correctly. And US printed dates don’t sort right either. So now to fix this, I have to us NSDateFormatter in reverse to create NSDate objects as I read in the database, and sort by them.

If you get the idea that I never found the perfect format, you are right. If you know of a better way to handle this, let me know. If you hurry, I might even be able to drop it in the next update.

Leave a comment

Filed under Uncategorized

Easy background Tasks in iOS 4

Now that my grouchy post about the problems with iOS 4 is out of the way, I thought it was time for a fun one. One of the apps I was working on when 4.0 came out had a download feature. Naturally, it made sense to continue downloading in a background task. But I wanted as few code changes as possible. Wrapping it all in start and end background task calls seemed excessive. And I had written my download class to be largely self-contained, and I did not like the idea of changing my code structure just to have it called by the app delegate methods. As often happens notifications made it easier.

The documentation briefly mentions the UIApplicationDidEnterBackgroundNotification as an alternative. Since my app was designed to run under 3.x as well (a quickly vanishing need), I ended up with this code in my download class init function:

        if ([[UIDevice currentDevice] respondsToSelector:@selector(isMultitaskingSupported)]) {
            bgTask = UIBackgroundTaskInvalid;
            [[NSNotificationCenter defaultCenter] addObserver:self
                                                     name:UIApplicationDidEnterBackgroundNotification object:nil];

Now, a nice non-UI class can easily respond correctly when the app goes into the background. Checking for a response from the UIDevice feels a little awkward, but it’s better than checking for version numbers directly. (Is there a way to check for the existence of a notification directly? It would feel cleaner.)

Now, what should we do when the notification happens? Well, if you simply want to continue your app for as long as Apple allows, here’s what your doBackground might look like:

- (void) doBackground:(NSNotification *)aNotification {
    UIApplication *app = [UIApplication sharedApplication];
    if ([app respondsToSelector:@selector(beginBackgroundTaskWithExpirationHandler:)]) {
         bgTask = [app beginBackgroundTaskWithExpirationHandler:^{
            // Synchronize the cleanup call on the main thread in case
            // the task actually finishes at around the same time.
            dispatch_async(dispatch_get_main_queue(), ^{
                if (bgTask != UIBackgroundTaskInvalid)
                    [app endBackgroundTask:bgTask];
                    bgTask = UIBackgroundTaskInvalid;

You might recognize this from the App Programming guide. But I pared it down so this code only starts a background task, and does not end it until the very last minute. This way, whatever your code is doing, it will just keep doing it. (BTW, this might be a really bad time to call a synchronous download method. If your app is taking too long to respond on the main thread to these notifications, you might see the screen go black when coming back from backgrounding.)

If your program downloads as much as it can forever and ever, you are done. (OK, you’ve made a horrible, battery sucking app, but hey, your done!) Since I did not think my client would like that, I had to add 5 more easy lines. (Our app still got rejected, but that was not my fault I promise. And don’t ask, I won’t embarrass the client.) These lines check to see if the download is done, and end the task.

    UIApplication *app = [UIApplication sharedApplication];
    if (downloadArray.count == 0 &&  [app respondsToSelector:@selector(endBackgroundTask:)]) {
        if (bgTask != UIBackgroundTaskInvalid) {
            [app endBackgroundTask:bgTask];
            bgTask = UIBackgroundTaskInvalid;

This code is in with my method to check if there are any more downloads to be done. Now, when I’m done, I end cleanly, and stop drawing battery power when there’s no more work to be done. Frankly, I was surprised how easy this was. And I only had to change my existing code at initialization, and when downloads were complete. Maybe iOS4 isn’t so bad after all.


Filed under Uncategorized

Editing 100’s of sound files

I’ve mentioned in a previous post that my latest app has 100’s of images. Well, it also needs almost the same number of sound files. And, as an independent developer, I don’t have the resources to get the sounds professionally made. So my friend and co-designer coached his girlfriend through reading all the words. She did an excellent job. But we could not figure out how to record them without extra silence at the end of each file. The extra silence caused strange delays in the app, so it had to be fixed. And it had to happen in a batch. Because I’m not doing 500+ files by hand.

Normally, when I need sound editing, I turn to Audacity. Thankfully, they added .m4a support just before I needed it. So I was able to take the Garage Band files, and use Audacity’s truncate silence effect. But repeating an effect over several files was a new trick for me. It turns out Audacity has a feature known as an Chain, which can be used on a group of files. Using “Edit Chains”, I was able to create a chain that removed silence, and removed an unneeded stereo channel at the same time:

Edit Chain showing remove silence.

But can you see the problem? Audacity does not support exporting as .m4a in a chain, so I had to export them all as .wav’s. This makes them too large, and hurts my chances for keeping the app under the 3G limit of 20M. So I needed another tool, preferably a free one.

If you have a UNIX background like I do, afconvert is the perfect tool. It’s a command line utility, included for free on a Mac, that converts sound files to any format with tons of options. I just needed it to convert back to a proper m4a. It turns out, I needed 2 options to do it, -f m4af to set the file format, and -d aac to set the correct data format inside the file. So I wrote the following script to convert all the files in a directory:

find . -name '* *.wav' | while read i
 afconvert -f m4af -d aac "$i"

If you are not familiar with BASH, the first line finds all the files with a space in the name, and .wav at the end. The filenames are then read into a loop variable, i, which I then use to run afconvert on the proper files. Don’t ask me why all the sounds had a space in the filename, they just came to me that way.

With these tools, I was able to remove silence from over 500 sounds in a very short time. While I doubt you’ll ever face exactly this problem, maybe one of these tools will help in your project.

Leave a comment

Filed under Audio

Undocumented Annoyances when moving code to iOS 4

Maybe it was the fact that AT&T messed up my upgrade date, so I don’t still have my iPhone4. But I think it’s the fact that I lost valuable time tracking down these issues when I moved my projects to the iOS 4.0 SDK. These 4 issues are undocumented changes that came up by simply moving a project running under 3.1.3 to be built under the 4.0 SDK.

1. The definition of shadows in Quartz has changed. They used to be drawn based on the direction of the light casting the shadow. Now, they are based on the direction of the shadow itself. There’s a post on the Apple forums claiming it is by design, and actually changed in version 3.2. So my nice custom drawn buttons all look wrong. And worst of all, the only way to support 3.1 is to detect the version number and change the sign of the y parameter. After all the forum posts where we were told to detect features, and not the version numbers. Grumble, grumble.

2. Using a custom UIButton in tableFooterView is displaying too large. I have an image that is 297 pixels wide, and I use it to make a custom button 297 points wide. And 4.0 on both the 320×480 simulator and the 3GS draw it much larger, so it can’t be a scale issue. I can’t tell how big, because it just goes off the screen. This is the most aggravating personally, because I still have no reason why this happened, and no one answered about it on the forums. After wasting time trying to fix it, I moved the button to the sectionFooterView, where it renders perfectly. It’s a good thing I don’t know any Apple engineers, I would have yelled at them about how much time this wasted.

3. Calling dismissModalViewControllerAnimated when there are multiple view controllers to be dismissed is suddenly calling “viewDidAppear” on all the controllers being dismissed. This would probably not be apparent, except that one of my view controller plays a sound everytime it appears. Now the sound is being played when it is being dismissed. This behavior is bizarre, and disconcerting because there does not seem to be another way to detect when a view controllers view is displayed. And, again, no answer on the official forums. I’m having to add a goofy BOOL property to the view controller that is only set when the controller is created.

4. touchEnded is being called differently. This one is actually working out to be a feature, but I can’t find it documented. Basically, under 3.x, if you press inside a view, and release your finger outside of that view, touchesEnded is never called. Under 4.0 it is. The reason this is a feature is that one of my apps is designed to be used by children who might not have the motor control to press and release inside the same view.  So under 4.0 they can use my control, whereas the 3.x version needed to be fixed. By duplicating the same code in touchesEnded and touchesCancelled, it looks like things are working correctly.

I know this is not an exhaustive list, and other people are having worse issues, like crashes and major visual issues, moving to 4.0. But even these 4 were enough for me to curse the new SDK. Of course, when my iPhone4 gets here, I’m sure I’ll get over it.

1 Comment

Filed under iOS4