Building an info screen for your iOS app using UIWebView

November 9, 2011 by · Leave a Comment
Filed under: iOS 

For my upcoming iPhone app I was looking for an easy way to add an info screen that describes the app and some of the features. I found a lot of mentions of “use UIWebView”, but I couldn’t find a comprehensive tutorial or list of steps. So here is a description of the implementation I came up with and some issues that I ran into.

Building the View

The view is relatively simple – all it has is an instance of UIWebView. Here is the header file:

#import <UIKit/UIKit.h>

@interface InfoViewController : UIViewController <UIWebViewDelegate> {
    UIWebView *webView;
}
@property (nonatomic, retain) UIWebView *webView;

@end

In the init-method, the UIWebView is instantiated and made to use the whole area:

- (id) init {
    [super init];
    if (self) {
        //set title to navItem
        [[self navigationItem] setTitle:@"Info"];

        // add webView
        CGRect bounds = [self.view bounds];
        bounds.size.height = bounds.size.height - 48; //height for navItem        
        webView = [[UIWebView alloc] initWithFrame: bounds];
        webView.delegate = self;
        
        [[self view] addSubview:webView];
    }
    return self;
}

I’m not really happy with the “magical“ 48 pixels to account for the navItem, but at least it’s working fine. (If you have a better way, please let me know!)

Displaying Info Text

Displaying HTML

Once the view is initialized, displaying an HTML-page such as help.html is pretty simple:

- (void) viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    
    //build a request to display help.html
    [webView loadRequest:[NSURLRequest requestWithURL:
            [NSURL fileURLWithPath:[
                [NSBundle mainBundle] pathForResource:@"help" ofType:@"html"]
                    isDirectory:NO]]];
}

You just have to add the file help.html to your project and make sure it is part of the target. It gets copied to the simulator or iPhone as part of the installation procedure. You can also include some CSS to properly style the html document (especially for aspects such as fonts or padding).

Displaying local images

In order to display images in the html, you just have to reference like this in the html-file:

<img src="ScreenshotTop.png" alt="Screenshot Top Part" width="288" height="214"/>

If you google for “UIWebView local image”, there are a lot of references to setting the baseURL of the UIWebView. I couldn’t get this to work properly, but instead found that simple references to png-files just work (whereas jpg-files did not work).

As with all images, they should be properly scaled to the dimensions that you want them to be displayed in order to avoid any re-sizing on the device which can take quite some time for screenshots.

Displaying the view

Adding an Info Button to the Navigation Bar

There is a standard info button (UIButtonTypeInfoLight) that is ideal for an info screen, but it can’t be used directly in a navigation bar. I order to use this standard button, you have to wrap this button in an UIBarButtomItem:

UIButton* infoButton = [UIButton buttonWithType: UIButtonTypeInfoLight];
[infoButton addTarget:self action:@selector(showInfo) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *infoBarButton = [[UIBarButtonItem alloc] initWithCustomView:infoButton];
[self.navigationItem setRightBarButtonItem: infoBarButton animated:YES];
[infoBarButton release];
[infoButton release];

Showing the info view

In order to show the view, you just initialize the InfoViewController and push it on the display stack:

-(void) showInfo {
    if (!infoViewController) {
        infoViewController = [[InfoViewController alloc] init];
    }
    [[self navigationController] pushViewController:infoViewController animated:YES];    
}

Handling Links

When you include links in your html-file (such as to your website, to your product FAQ on the web or a mailto: your support email address), you want them to open in the corresponding app (and not in your UIWebView). In order to do that, you have to implement the UIWebViewDelegate protocol in your InfoViewController, especially the following method:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType; 
{
    if ((navigationType == UIWebViewNavigationTypeLinkClicked) || ([request.URL.scheme isEqualToString:@"mailto"])) {
        [[UIApplication sharedApplication] openURL:request.URL];
        return false; //will open links in Safari or MailApp
    } else {
        return true;
    }
} 

This method is called whenever a request starts to be loaded in the UIWebView. It looks if is called because a link was clicked (NavigationTypeLinkClicked) or the URL scheme is “mailto”. If that’s the case then we open that link in the “sharedApplication” which is Safari (for web pages) or the MailApp (for mailto’s).

Updating SmarterSteuer App for the iPhone – Adding a settings dialog

August 6, 2011 by · Leave a Comment
Filed under: iOS 

After more than four months that my SmarterSteuer app was on the AppStore, I decided it was time to extend the application a bit. To recap, SmarterSteuer helps freelancers and self employed people like myself to figure out how much tax is owed and allowing them to save some money. V1 is a pretty simple app – just one screen, no options or sub-dialogs.

Kirchensteuer

The addition I had in mind was with regard to “Kirchensteuer” (church tax). In Germany, when you are a member of one of the registered national churches (either Catholic or Protestant), the state collects a certain percentage and forwards it to the appropriate church. The amount of church tax is based on the income tax, but the percentage is based on which state (“Bundesland”)  you live in (either 8% or 9% of the income tax). Also, when you’re not a member of a church, you don’t have to pay church tax at all.

So in it’s simplest form, the user has to make a choice between three different options:

  • no tax at all (when you are not a member of a church)
  • 8% church tax (when you are living in Bavaria/Bayern or Baden-Württemberg)
  • 9% church tax (when you are living anywhere else).

But then the user would have to know whether she lives in a high or low rate state. I could add some text to explain which is which, but it wouldn’t be very intuitive.

 

Iteration 1 – A simple dialog

The first step was to add a working dialog that allowed the user to set the appropriate options. After thinking about it, I added two main input elements:

  • a switch that allows the user to set whether she has to pay church tax at all (“Kirchensteuer-pflichtig”)
  • a picker that allows the user to set in which state she lives (“Bundesland”)

The resulting tax rate (“resultierender Satz”) is then displayed at the top.

Here’s a screenshot of what it looked like:

image

I spent some time to make the dialog fully functional and was almost ready to release the app like this. One of the things I didn’t like was that the picker didn’t offer too many options for styling, so I was stuck with darker grays than I would have liked. I looked around a bit, but I couldn’t find any simple way to change this. (Maybe Apple will add some functionality to the picker class in later releases.)

But what kept me from releasing this version was the fact that this dialog does not look anything like the “main dialog” of SmarterSteuer which is a TableView:

SmarterSteuer_Screen

Iteration 2 – Using a TableView

The next iteration consisted of developing a TableView that gives the same options to the user. As with the main dialog it used two section within a TableView – one for displaying the resulting tax rate, and a second section for the different options. Applying some styling, and the dialog looked very much like main dialog:

image

But there was another problem: If you selected one of states towards the bottom of the list (like Schleswig-Holstein where I live), the first section displaying the results scrolled off-screen:

image

There might have been some ways of getting around that (e.g. splitting the dialog into two TableViews instead of two sections), but that sounded like it didn’t quite fit Apple’s User Interface Guidelines (a TableView is always full screen). It just felt that a TableView wasn’t a good fit. So on to …

Iteration 3 – Trying to fake a TableView

Using some “background” buttons, I could make the simple dialog from Iteration 1 look like a TableView:

image

This was very close to what I wanted, but I couldn’t really style the buttons with the proper background color. I’m not really sure why it didn’t work (see StackOverflow question #6496441), but I just couldn’t make it work.

Iteration 4 – TableView with custom cells

So instead of faking the TableView, why not build a real TableView with custom cells? It was a bit of work, but I managed to get it to work properly:

image

The tricky bit was to make sure that any gestures on the picker did not get picked up by the TableView. The only solution I could come up with was to set the whole TableView to scrollEnabled = NO. This works because the TableView is not larger than one screen, but it feels a bit like a hack. If you’ve got a better idea, look to my StackOverflow question #6711878.

Finally, this version was good enough for me to release it as version 1.1 of SmarterSteuer. It is available on the AppStore!

ProgressView in iOS

April 28, 2011 by · 2 Comments
Filed under: iOS 

When you are doing some longer running operation in a program, you want to give some feedback to the user. This blog post looks at some ways on how to do this in iOS. I’m using my ongoing work on my next iPhone app “SmarterContacts” [include link] which will find and eliminate duplicates in your iPhone contacts.

Providing meaningful progress feedback to the user

One of the strengths of the iPhone interface is its constant and almost immediate feedback to the user. For example, if you scroll through a long TableView, it feels as if you are grabbing the contents and “throw” it up. You can tell that a lot of work has been put into this user experience, and it’ obvious that you should avoid breaking this feedback loop when writing your own apps.

But sometimes there are operations in an app that are going to take some time. As a rough guideline, anything that takes longer than a few tenths of a second, there should be some feeback to the user that the program is doing something and hasn’t crashed. iOS offers some way of providing this feedback:

  • ActivityIndicator image
    In iOS, this is an animated “spinning wheel” that shows that something is still going on. Other OSs use the same idea, but a different metaphor (OSX has the beachball on earlier Mac versions it was watch, Windows used to have an Hourglass and now has a rotating orb.) The ActivityIndicator is the simplest way to provide feedback – you just add one to the current view, start it spinning, do your operation and then remove the it.
    One important drawback is that the ActivityIndicator doesn’t give any feedback to the user about actual progress, i.e. how far along is the operation and how much longer is it going to take. This is not a problem for operations that are reliable and take just a few seconds. But for anything longer, the user might give up on the program.
  • ProgressBar image
    This is a bar that starts empty and gets filled when the operation is progressing. When it is completely full, the operation is completed. (A little side note: In some dialogs – notably in older Windows file copying dialogs and a lot of installation programs – the bar empties after it has been filled and then starts to fill again, over and over. This degrades the ProgressBar to just an ActivityIndicator with all its disadvantages, but using a lot more space and disappointing the user who will think the operation is done when the bar is filled for the first time. Definitely not recommended.)
    The ProgressBar provides much more feedback to the user: It shows that the program is still making progress as the bar nudges on and on, and it allows the user to make a rough estimate of how much work is left. However, it requires a bit more work by the programmer to update the ProgressBar. It also requires the programmer to figure out what “80%" done” means – this is sometimes tricky when there are a lot of different steps involved.

In my app one long running operation is comparing the different contacts and figuring out how “similar” they are to one another. As this requires comparing each record to all other contacts, the program has a characteristic of O(n^2) which quickly gets noticeable when working on more than 100 records. Therefore, I want to provide feedback to the user while this operation runs. As this takes more than a few seconds (even in the simulator, which is a lot faster than my iPhone 3GS), I wanted to use a ProgressBar. I also wanted to provide some additional feedback (such as “at record x of y” and “z seconds remaining”), so I built a nice looking ProgressView.

A first stab at a ProgressView

The ProgressView looks like this:

image

It consists of a few labels (for the title, the number of records and the remaining time) and a ProgressBar. The trickiest thing here is the positioning of all the items on the screen. (I’m sure the code I have is a bit clumsy and can be improved. Suggestions are welcome, please add a comment.)

//add top Label
topLabel = [[UILabel alloc] initWithFrame:
                     CGRectMake(0, gutter, width-4*gutter, 20.)];
[topLabel setCenter: CGPointMake(center.x, center.y - 30.)];
[topLabel setText: @"[Being busy]"];
[topLabel setTextAlignment:UITextAlignmentCenter];
[self addSubview: topLabel];

//add Progress Bar
progbar = [[UIProgressView alloc] initWithFrame:
                           CGRectMake(0, 0, width-4*gutter, 20.)];
[progbar setCenter: center];
[progbar setProgress: 0.];
[progbar setProgressViewStyle:UIProgressViewStyleBar];
[self  addSubview: progbar];    

//add progress Label
progLabel = [[UILabel alloc] initWithFrame:
                      CGRectMake(0, 0, width-4*gutter , 20.)];
[progLabel setCenter:CGPointMake(center.x, center.y + 10.)];
[progLabel setTextAlignment:UITextAlignmentCenter];
[progLabel setText: [NSString stringWithFormat: @"At record %i of %i", currentValue, maxValue]];
[self addSubview: progLabel];  

// add remaining time label
remainingLabel = [[UILabel alloc] initWithFrame:
                           CGRectMake(0, 0, width-4*gutter , 20.)];
[remainingLabel setCenter:CGPointMake(center.x, center.y + 30.)];
[remainingLabel setTextAlignment:UITextAlignmentCenter];
[remainingLabel setText: @"Calculating remaining time ..."];
[self addSubview: remainingLabel];  

It is displayed as a little “dialog box” on the screen and “dims” the view that it is called from by using a semi-transparent background:

[self setBackgroundColor: [UIColor colorWithRed:204./255 green:213./255 blue:216./255 alpha:0.5]];

The ProgressView can be updated by calling the “setCurrentValue” method which then updates all the labels and the progress bar:

-(void) setCurrentValue:(NSInteger)newCurrentValue {
    currentValue = newCurrentValue;
    if (currentValue % stepValue == 0) { //% is modulus
        [progLabel setText: [NSString stringWithFormat: @"At record %i of %i", currentValue, maxValue]];
        [progbar setProgress: (float) (currentValue) / maxValue];
        
        if (currentValue > 0 && abs([startTime timeIntervalSinceNow]) > 0) {
            int remaining = trunc (abs([startTime timeIntervalSinceNow]) 
                                   * (maxValue - currentValue) / currentValue);
            [remainingLabel setText: [NSString stringWithFormat: @"About %i seconds remaining", remaining]];
        } else {
            [remainingLabel setText: @"Calculating remaining time ..."];
        }
    }
}

All that’s left to do, is to open the view at the start of the long-running operation using addSubview: and then update the ProgressView by calling setCurrentValue:. When the operation is done, we just remove the ProgressView using removeFromSuperview.

This almost works .. the ProgressView is displayed at the start and removed at the end of the operation, but then it is not updated during the operation – which means no feedback to the user and it looks like the program crashed.

Making it work using threading

The reason for this behavior is that the screen is not updated while the operation executes – I’m not exactly sure when the update would occur (unless when the whole operation is over). This is because the simple program is unthreaded, i.e. there is no thread that is available to update the screen. In VisualBasic there is the expression “Do Events” which basically turns control over to the OS to process screen updates, user inputs etc.  There does not seem to be an equivalent expression in iOS, instead we have to delve a bit into threading to separate between the screen updates and the calculation.

Setting up the threading environment

In earlier versions of iOS it was quite a lot of work to set up the threading environment. Since version [???] this has been made a lot easier by providing NSOperationQueue and NSInvocationOperation. Now all that is left to is:

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

The NSOperationQueue basically set ups the threading environment, so that as soon as an operation is added to the queue, the execution is started in a secondary thread.

Updating the ProgressView

This secondary thread is then executing the long running operation, while the primary thread can still update the screen. Within our operation we can trigger the screen update in the main thread:

[self performSelectorOnMainThread:@selector(updateProgress) 
                       withObject: nil waitUntilDone:YES];

I’ve had some problems passing an object using the “withObject:” selector, probably because I was using memory that was only available to the secondary thread. (Probably because it was allocated by the secondary thread.) Instead, I was able to use a property of the view that created the secondary thread.

Tearing down

All that was left to do was to remove the view when the operation is done. This is achieved by calling

[self performSelectorOnMainThread:@selector(calculationDone) withObject:nil waitUntilDone:YES];

Again, this calls the main thread to notify that the calculation is done and that the ProgressView can be removed.

Summary

We’ve managed to build a good looking, informative ProgressView. In addition to implementing the code to display, update and remove the view, we also had to make sure that the ProgressView is updated on the screen by creating and using a secondary thread. The additional code required for the threading was quite small, and it was able to use the original, unchanged code for the ProgressView.

Duplicates in Address Book: Some Progress on my next iPhone App

April 5, 2011 by · Leave a Comment
Filed under: DataQuality, iOS 

After trying out a few things during my last train rides to my consulting engagement, my next iPhone app is slowly taking shape.

Development progress

Here is a very rough, “in work” screenshot of what I have for now (using some test data):

image

Each cell in the TableView represents the contacts in the address book. The number in braces shows how “similar” another contact record is. (The number shown is for the “closest” record.) I want to make things a bit easier to spot by coloring the cells (e.g. red = 100% match, yellow between 90 and 100% match). I’ll also add some way to filter the displayed records (i.e. display only records above a certain threshold).

In the detail view, I want to display the “closest” records for one address and provide some way to look at the details of the involved contacts. (As this might require quite some back and forth between the different contact records, an iPad with additional screen estate may be a good idea here.) The detail view should also have some functionality to “remove” unwanted records (duplicates) or “merge” two records (collect all different phone numbers, email addresses etc. into one contact record and remove the other one).

Obviously, there is still a lot of work to do, but I’m making some progress. While I’m still looking for a good name, I already have an icon for the app: colored_background_clear_glass_512

Emerging Tiers

I’m also getting a clearer picture of which different modules I can offer:

  • Comparison – reading the address book and figuring out which records are close
  • Removing exact duplicates – Often, there are exact duplicates in the address book (for example after some technical problems with synching). These should be relatively simple to identify and delete.
  • Merging contact records – This is a lot more complicated as there are quite a few different scenarios. For example, you have to pick a “surviving” name (as there can be only one name), but there can be multiple addresses, phone numbers or email addresses.
  • Standardization – For some fields, different content can mean the same thing, e.g. “1231234567” is the same phone number as “123 123 4567” or “(123) 123-4567” or “+1-123-123 4567”. I’m not sure how much there can be done here, especially in addresses (Memory Lane = Memory Ln).
  • Once I get started, there’ll probably be more modules that make sense in the given context.

However, this looks like a good way to provide different tiers of the app:

  • entry level (low cost, maybe even free) – just offering the base comparison module
  • mid level – comparison plus dealing with exact duplicates
  • pro level – all modules as outlined above

This will also be a good scenario for “in app purchases” that I want to look into.

Idea for my next iPhone App: Duplicates in your Address Book

March 19, 2011 by · Leave a Comment
Filed under: DataQuality, iOS 

My Tax App “SmarterSteuer” has been in the iTunes App Store for almost a month now. Of course, the sales are not even remotely close to allow me to quit my day job – at 1.59€ and about 25 sold apps I haven’t even recovered the cost for my membership in the Apple development program. Nonetheless, I’ve been impressed with the number of sales and the pretty level number of sales from day to day: The tax app is quite limited to a certain type of user (freelancers that have recently started and are trying to figure out their taxes) and it’s only applicable to Germany (and it’s only available in the German App Store). I’ve been trying to guesstimate the market size for a general purpose app that would be sold worldwide.

Guessing a Market Size

I’ve been trying to find out how many iPhones have been sold worldwide and in Germany. Apparently, Apple releases quarterly numbers (compiled in a nice sales graph by wikipedia) which would put the number of iPhone sales worldwide to about 90 million at the end of 2010. I couldn’t find any matching numbers for Germany, the latest number I could find was 1.5 million at the end of 2009 where the worldwide number was at about 40 million. That would put the worldwide market at about 27 times the German market which is consistent with a 4% market share that admob is estimating for Germany. Let’s work with a nice round factor of 25. 

It’s even trickier to estimate what part of the current German market would consist of freelancers. In the general German population you have roughly 1 million of freelancers (with 81 million Germans total). I’m assuming that that percentage would be mich higher among the owners of an iPhone. Just for arguments sake, I’m working with a percentage of 10% of freelancers in the whole iPhone market in Germany.

If we take these two numbers, we come up with a factor of 250 – i.e. the market size of a general purpose worldwide app is about 250 times as large as the market that I’m currently addressing with my text app. This may not translate to 250 times as many sales or 250 times the revenue, but even 100 times the sale of my current app would be quite impressive.

I’m not sure how if these numbers are reasonable (please let me know if you have better numbers or estimates) – but it’s certainly encouraging enough to contemplate writing a general purpose app that can be used worldwide.

App Idea

I’ve got a few ideas that come close to the description of a general purpose app with a worldwide appeal, but the one that fits best is an app that analyzes the iPhone’s Address Book and looks for duplicates (either created because of synching problems or by manually entering the same person twice). So here is a short description of that idea:

Level 1:

  • look in your address book for potential duplicates
  • visualize the “duplication” in a nice way so the user has a chance to follow up on the results by manually editing/deleting the records

Level 2:

  • offer a way to “automagically” delete complete duplicates (i.e. record identical in all fields)
  • offer a way to merge records (i.e. if the names are the same but each address is different create a record with both addresses so the user can manually edit the “survivor”)

Level 3:

  • add-on services such as address validation or other ideas that come up in the meantime

I think that this may be a valuable app, something that people are willing to pay for. I’m not sure about the effort required to develop such an app, but I’m ready to start. Most of the technology should be pretty straightforward (accessing the address book, comparing records/strings etc.). I’m going to mull this over for a few more days, but I think I’ll get started as soon as I have some time available.

Looking for duplicates: Results of a simple algorithm

February 10, 2011 by · Leave a Comment
Filed under: DataQuality 

As a little side project, I’ve been working on analyzing the finishing times in Ironman Triathlons. (If you’re interested, please head over to my Triathlon Rating site. This involves getting race results and trying to match the athlete names in order to figure out different results from the same athlete.

Finding Candidates

In a first, relatively simple implementation, I’ve used Excel to group results from athletes with exactly matching names and only corrected some obvious issues. One example is German umlauts (äöü). There is an athlete named “Mühlbauer”, and there are a number of different spellings (Muehlbauer, Muhlbauer, other strange representations that seem to indicate encoding problems like M&uml;lbauer). Another typical issues is abbreviations of first names (Timothy DeBoom and Tim DeBoom).

However, this was a completely manual process, and while it’s next to impossible to built a completely automated solutions, I wanted some automated help. So I’ve built a simple implementation looking for duplicates within my data that performs the following checks:

  • Is one athletes name a substring of another athlete’s name? (Example: “Osborne, Steven” and “Osborne, Steve”)
  • Are two athletes names very similar (using the Levenshtein distance)? (Example: “Csomor, Erica” and "Csomor, Erika”)

Both checks are performed matching firstname and lastname with each other and “crosswise” (firstname with lastname and lastname with firstname). (Example: “Gilles, Reboul” and “Reboul, Gilles”) I should add that checks using just these two fields will not be sufficient for a typical business scenario, other fields have to be taken into account (for example birth date or address).

After implementing these relatively simple checks, I found a couple of “pairs”, ranging from pretty obvious to borderline cases (Chris Brown and Christopher Brown could be the same person, but could also be two different athletes). All in all, I was able to identify 11 pairs that are in fact duplicates, representing 1.7% of my 632 athletes. I found this number to be quite surprisingly large – I would have guessed that the number would be smaller: with the small number of athletes, just one person (me) adding athletes to the database and the manual checks I had already performed. The typical situation in a business would be more conducive for adding duplicates (larger pool of records, a lot of people adding data, users not quite as diligent as I thought I was).

Once the pairs are identified, there has to be a manual step to determine if the pairs are indeed duplicates of one another. This is very hard to be done by an algorithm, there are just too many scenarios to consider.

Survivorship

Once the duplicates are determined, the issue of survivorship comes up – i.e. identifying the “best” record that should be used in the future. In a business context, there are some automatic steps that can be performed (for example collecting the different fields that are filled in the records). When having to decide between different values, there may be some more help available for limited areas (for example when identifying valid addresses). But typically, when making a decision which value is right a human has to be involved (Erica or Erika?).

What to do with the Duplicates?

Once the survivor is clear, what to do with the duplicates is still open. In my case, I could just change the race results to point to the “right” athlete record and the other record could be deleted. In business cases, this may be a bit more difficult to do. For example, changing the owner of an account in a banking system is quite an involved procedure (and may even involve some communication with the customer). Also, deleting partners may not always be possible – in the banking example, you probably want to preserve the fact that for a time some other “record” was the owner of an account. In these cases, the minimum you want to do is to “mark” the duplicates in a certain way (so that it is not used anew) and you also want to point to the survivor ( so people know which is the right partner to use and some systems may provide a unified view on these two partners).

Most of the times, correctly updating the system to reflect that duplicates and survivors have been identified is a highly manual process, especially if there is a complicated IT infrastructure with a number of different applications involved. I’m not aware of any general tools that help with this.

Summary

Using my example, a relatively simple algorithm already provided good results in identify duplicate candidates. The percentage of 1.7% that I found in my data is probably a low estimate for bigger, commercial data. I was able to deal with the candidates pretty easily, in a commercial environment this would have been a lot more complicated. 

Steuer App: Almost MVP

February 1, 2011 by · Leave a Comment
Filed under: iOS 

After working on my little side project TriRating over the holidays, I’ve gone back to my iPhone app.

It’s close to being ready to be a “Minimum Viable Product” MVP:

image

Compared to my last blog post, I’ve changed the colors of the interface with a little help from Zsolt Markus (who I contracted through oDesk), but mainly I made the interface work correctly. I think there is very little left before I’m going to release it (little things like formatting of the numbers and remembering the last  values when the app restarts).

It’s not yet the cool app I had in mind, but it’s looking good enough not to be embarrassed by it and I just want to get the app out (and hopefully get some feedback). I’ll play around a bit more to work on gestures and ease of input. For now,  I’ll also have to  work on the non-coding aspects (e.g. companion website, sales copy on iTunes, support infrastructure).

Steuer V0.2 – making progress with my iPhone app

December 28, 2010 by · Leave a Comment
Filed under: English, iOS 

Over my last few train rides I’ve managed to make some progress in making my app a bit nicer.

This is what the old version looked like:

image_thumb2

Apart from a few “under the hood” things (like moving to a new version of XCode and the SDK, so the simulator looks like an iPhone 4 now), I’ve improved on the following items:

  • switched to a table-based view
    This makes the inputs (Eingaben) and results (Ergebnisse) pretty self evident. I’m still going to tinker a bit with the view as there is one row that wouldn’t quite fit on one screen and I don’t want the app to start scrolling.
  • added a “title bar” (called navigationItem in iPhone-speak)
    I couldn’t quite figure it out how to do that for the simple version where I didn’t have to write a controller for the main app screen.
  • ditched the “Berechnen” button
    After changing the inputs, the results are automatically calculated.

Here’s where I am now:image

I think that is looking a lot better than V0.1, but there is still a lot to do – both from a design (colors anyone?) and a functionality standpoint, but at least I’m making some progress. I’m hoping to get a bit more done in the “free” days after Christmas, but it already looks like a lot of days are filled with visits to family and friends, and that is certainly important, too. I’ll keep you posted!

Unit Testing an iPhone App – Not important to Apple?

December 7, 2010 by · Leave a Comment
Filed under: English, iOS 

After the base functionality of my iPhone app was in place, I wanted to have a closer look into unit testing. I try to use unit testing where it makes sense when developing for my customers, so I wanted to do that for my own app as well. I was encouraged that there was some documentation in the Apple developer’s guide, and I was really looking forward to putting the framework to use.

Restructuring my App for testability

Before being able to properly test the tax calculation functionality, I had to a bit of restructuring to do in order to properly isolate the functionality I wanted to test and to separate it from the GUI. I pulled the tax calculation into its own class and made the GUI use this TaxCalculator. This separation of concerns is one of the benefits of using a Test Driven Approach – it forces you to think about testability and separation of concerns if you want to have an easily testable class. The changed were totally unnoticeable to the user, but added some value because the code is now much easier to understand and to maintain. It took me less than an hour of development  time, and that was time well spent.

Initial Problems with SDK 4.1

The next step was reading up on Unit Testing with the iPhone SDK. There is a chapter in the iOS Development Guide that describes how to add tests to your project. From the start, I was getting strange errors even though the tests seemed to pass. As I was developing these tests while riding the train, I played a bit around in order to avoid the problems, but didn’t have any success. I added a few tests for the TaxCalculator, but ran into some more strange behaviour.

Some Progress with SDK 4.2

When I was back home, I googled for my problems and quickly found that it was a problem with SDK 4.1 (thanks to this Stackoverflow question). There seemed to be a workaround in 4.1, but as the problem was fixed in SDK 4.2, I just decided to upgrade my SDK which I wanted to do anyways.

After that, I had the next problem – XCode was telling me that my “base SDK was missing”. Googling for that problem I quickly found an easy fix for this issue as well (the one I used was on John Alexander Rowley’s blog, but there is a number of helpful hints across then net. After that, my tests were finally running without any errors and I was finally able to focus on writing test cases.

However, I soon ran into a lot of problems in properly checking what the tests were doing and if my app was working fine. I’m usually trying to reserve judgment, but in this case I can’t put it any other way:

What was Apple thinking?

Using the Apple supplied Unit Test library, the tests are only run during the build phase. Running tests during the build phase is not a bad idea, it can prevent building an app if some tests are failing. But running it only during the build phase has some serious drawbacks: Messages sent to the log do not show up in the debugger console (at least they show up in the system console), but more important: You can’t debug the tests!! (I should note that there are some descriptions on how to set up a separate target that would allow you to debug the tests, but the procedure is very complicated – like setting the environment variable CFFIXED_USER_HOME to "${HOME}/Library/Application Support/iPhone Simulator/User". I gave up on it after unsuccessfully trying for an hour or so.)

I have no idea how anyone can think that what is currently delivered is good enough to use. No one who has never used Unit Tests and pretty much wants (or has) to use it will endure the procedure to make it work. It just seems to be some item on a list that Apple wanted to tick (“Unit Testing? We have that.”) but wasn’t really interested in. Very “un-Apple” …

Switching to GHUnit

Luckily, there seems to be a good open source alternative to the Apple Unit Testing framework, name GHUnit (named after it’s developer, Gabriel Handford) which can be found at http://gabriel.github.com/gh-unit/index.html. It was very simple to download and add to my project. Within 15 minutes I had my project use GHUnit and I was able to run the previously developed tests within the simulator:

image

It also enabled me to log to the console and debug my test cases in the XCode debugger just like any normal app – without having to jump through any hoops. You may argue for a nicer interface (color anyone?), but that is certainly a minor issue. After the disappointing Apple framework, GHUnit delivered on what I was looking for. I’m hoping that the framework will hold up under all the testing I want to throw at it in the next weeks!

Calculating tax owed – My First iPhone App

November 2, 2010 by · Leave a Comment
Filed under: English, iOS 

As indicated in my latest blog posts, I’ve started to dabble in iPhone development. As a starting point, I have programmed a little app that calculates income tax owed based on money earned.

Use Case

The use case for this app is based on my personal situation as a freelancer in Germany. The situation is probably similar to other countries, and I might build similar versions for non-German markets.

As a freelancer in Germany, when you get paid, there is no tax withheld as it would be when you are an employee. At the end of the year, you have to declare your earnings, tax is assessed and then you have to pay. As this is a substantial part of your earnings (30-40% is a typical number here in Germany), you better make sure that you know how much of your earnings is actually money you can spend. It is also a good idea to put away the money you expect to pay for taxes. (I like to save the amount in a money market account, so I even have a chance to earn some income on it, even if there’s not much to make with the way interest rates are these days.)

So the App basically asks you how much money you have earned in a month and shows you how much taxes you would owe on this amount. Sounds really simple, but nothing with taxes is ever that simple:

  1. There is some additional sums that has to be included in the taxable amount. In Germany, the main one is called “Geldwerter Vorteil” or GWV for short – a monetary equivalent for non-monetary benefits that you get. The “classic” example is a company car that you can use privately or for riding to your place of work.
  2. There are different components to income tax, such as the base rate, “Solidaitätszuschlag” (an add-on income tax that was originally introduced to aid East Germany after unification) or “Kirchensteuer” (“church tax” which is collected by the state and then re-distributed to the churches).
  3. The tax rate itself is not one simple formula but has different formulas for different ranges.

So even if the use case is not all that complicated, it is also not trivial to build a decent app for that.

The App V0.1

Here is a screenshot of the first, simple version of the app:

image On the top, you can enter the money earned (“Einnahmen”) and the GWV. Below the ‘Berechnen’ (calculate) button the different components of the tax and the full amount of tax owed is shown (“Gesamt-Steuer”). In addition, the tax rate and the earnings after tax are displayed.

From a functional viewpoint, there is very little that is missing for the use case I outlined above:

  • Kirchensteuer is dependant on whether you are a member of a church and your location. There has to be some configuration, probably in a new view.

There are a few things that would be nice to have:

  • The last values should be saved and reloaded after the app start.
  • In order to calculate GWV for car use there are some standard rules. Instead of entering the final value, the way this is calculated should be made available .. another configuration view.

After that, the improvements are not so obvious:

  • make the interface prettier (icon, title bar, colors)
  • use better formatting of the values (separators, currency symbol)
  • instead of individual labels and fields try out a table-based view (should look “nicer” and also offer an easier way to integrate the configuration views)
  • explore a subview with ads in order to monetize the app (instead of selling it)
  • explore alternatives for smarter entry of values (instead of keyboard, use dials and/or gestures)

The last item is especially important for me, as I want to develop apps that make good use of the iPhone’s capabilities. In my view, today there is not much use for simple data entry iPhone apps that could just be a web form. In order to delight the user, you have to be able to make things better, easier and unique to stand out from the crowd of apps in the app store. This may be tricky for a “boring” tax app, and you also have to find a balance between “clever” and “easy to use”, but I’m willing to explore these areas and see what I can come up with.

I’ll post some news once I’ve improved the “raw” app I have now.

« Previous PageNext Page »