iPhone Code Evolution

Pages

Terry On Monday, February 7, 2011
Evolved Code #2
A Splendid Approach For Automating Background Tasks

I love drop-in modules. I love being able to grow the functionality of an application without having to do much work! But in the suggestions of how to write iPhone apps, I haven’t seen very many ideas about how to write code that allows re-use gracefully.

For example, in one of my apps I need several activities to begin whenever the app is loaded, or becomes active:

1. I want user uploads that did not complete to re-try being sent.
2. I want to check the server for updates, and download new files.
3. I want GPS location data to start being gathered.
4. In-App purchases should be re-attempted if previous calls failed.
5. I have some singleton classes that should be initialized.
6. Some data structures need to be pre-loaded with data, so screens load quickly when the user navigates to them.

And when the app is about to enter the background, there are tasks that need to gracefully end or pause themselves.

As I write new apps, many of them have exactly the same or very similar requirements. Most non-trivial apps will all have those six tasks, as well as others of their own.

For the sake of discussion, I will introduce you to a class I wrote that processes web service calls in a manner that handles interruptions, delays, and lack of connections. This class is called “RobustWebService” (or RWS for short). I will reference it below, to demonstrate this technique. What is important to know about this discussion is that RobustWebService has specific tasks to perform at specific times:

* When a web service call is made, it is added to a pending queue of operations that processes in the background.
* When the app enters the background all calls are canceled, and archived to disk in a “pending” state.
* When the app returns to the foreground all archived web service calls should be re-queued.


If you google around for solutions to having code execute in response to app state changes, you’ll find code examples where you add in calls to your code into your app delegate, because it gets called whenever the application didFinishLaunchingWithOptions, or didBecomeActive, or WillEnterBackground.

But let’s say you start working on another app, which re-uses a lot of your code. Wouldn’t it be nice if you could just copy over some files and get all the functionality, without having to worry about re-wiring up the app delegate methods?

To summarize, we want drop-in modules that, just by the virtue of being in the project, will perform their work, without any additional effort or wiring up. At this point you may be saying “and I also want unicorns and elves to bring me a pizza”. Bear with me.

I’m not sure how I missed this when reading the docs, but when iOS applications change state, it isn’t just the app delegate that knows about it. NSNotifications also get posted for all major state changes. Here is text taken directly from the api reference:

After calling [applicationDidBecomeActive], the application also posts a UIApplicationDidBecomeActiveNotification notification to give interested objects a chance to respond to the transition.

Similarly, there is a UIApplicationDidFinishLaunchingNotification that gets posted immediately after the application finishes launching, and other notifications for entering the background, or entering the foreground.

So this simplifies our code: instead of having to call
[RobustWebService handleAppBecomingActive]
in the app delegate’s implementation of applicationDidBecomeActive, we just have to have RobustWebService respond to UIApplicationDidFinishLaunchingNotification.

Now pondering this a moment, you might realize that in order for the RWS class to handle the notification, it has to register as an observer. That call looks something like
[[NSNotificationCenter defaultCenter] addObserver:self
                    Selector:@selector(handleAppBecomeActive)
                        name:UIApplicationDidFinishLaunchingNotification
                     object :nil]

And where can that get done? Remember, we do not want to touch the app delegate because that would defeat the intent of having a self-contained drop-in module. If only there were some way to have the addObserver function call happen automatically for the class. If only…

At this point we have to drop out of “Cocoa” and dig into the underlying technology of Objective-C. Sure enough, there is a class method, called “load” which, if present in your class definition, will automatically be called when the class is first loaded. Let’s restate that in code. If you write this function in any class.m file
+ (void) load
{
    // stuff
}

...it will run when the class is loaded by iOS. Interestingly enough, it is run before your app’s main() routine is called so you have to be very careful about what you try to do! Most of your app is NOT actually up and running at this point, but you are guaranteed that all frameworks that your class links too will be loaded first. Frameworks such as NSNotificationCenter, so if you include this in your class.m
+ (void) load
{
[[NSNotificationCenter defaultCenter] addObserver:self
                    Selector:@selector(handleAppBecomeActive)
                        name:UIApplicationDidFinishLaunchingNotification
                     object :nil]
}

Then your handleAppBecomeActive method WILL get called when your app becomes active, without you having to do anything other than include the class.h and class.m in your project.

And if you include this code in your class.m file

+ (void) load;
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handleAppLaunched) name:UIApplicationDidFinishLaunchingNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppResigningActive) name:UIApplicationWillResignActiveNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppBecomingActive) name:UIApplicationDidBecomeActiveNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppEnteringBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppEnteringForeground) name:UIApplicationWillEnterForegroundNotification object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleAppClosing) name:UIApplicationWillTerminateNotification object:nil];
}

Your class will get notified of all app state changes, with no other work required.  All you have to do is write the methods you want to be called when specific app state changes occur, and they will be called. This is so cool that it still makes me feel tingly. Enjoy!

Terry
Terry On Sunday, May 16, 2010
Is there anyone who isn't aware of the Apple store "In-App Purchase" (IAP) model?  I'm going to assume you have at least heard of it, and would like to integrate it into your app.

The attached code provides you with everything you need to get IAP working in your application.  No big deal there, there is lots of code scattered around the web that does the same thing.

What this code does, that I haven't seen anywhere else, is allow you to test out the Apple Storekit functionality in the simulator.  Let's examine the typical programming scenarios I face in developing an IAP capable iPhone/iPad app:
  1. I had code which is only supposed to function if the app hasn't been purchased, i.e., "Lite" functionality.  I needed to test that this worked on multiple runs.
  2. I had code which is only supposed to function if the app has been purchased, i.e., "Full" functionality.  Once you've got the Lite code working, you need to test that Full works just as well.
  3. I exercise my apps a lot in order to test various branches, so it must be easy to reset the state of the purchases.  Most of the StoreKit examples around the web record the app purchase in NSUserDefaults and to clear that you must delete the app from your device and re-load it.  I wanted the code to be able to reset the purchase flag just by being re-run.  While this didn't preclude the use of NSUserDefaults, I at least needed a mechanism to reset the flag.
  4. I want to run the code in the simulator.  One reason is that the NSZOMBIE code can only be run on the simulator.  Another reason was I needed to monitor the application's document directory while running, which you can't easily do on an actual device.
  5. Until I had the code relatively mature, I wanted the code to act as if a purchase occurred without actually making the "real" calls.  Otherwise the App store would have remembered that I had purchased the app on subsequent runs, and not allowed me to test certain branches, such as failed transactions.  This same approach came in handy for letting my early Beta testers "purchase" the app without actually doing so.  

The attached code does all that.  One caveat is that I made no attempt to support "subscription" IAPs.  Mostly because I don't use them, and they require a large support infrastructure to make them work.  I think the vast majority of small-to-medium apps will only ever use consumable, and non-consumable IAPs.

To use the code, add in the files under the group "Simple Store" to your project.  Modify your app delegate to call [Purchase setup] in "didFinishLaunchingWithOptions" and have a purchase button somewhere in your app that loads the PurchaseScreen controller, as the example app does.  Modify Purchase class to use your purchase ID, and you are finished.

With both SimpleStore DEBUG flags to YES your app will start up thinking it isn't purchased, and (on the simulator) you can exercise your Lite code, then "purchase" it and test your Full code.

When you are happy with the app code, and want to run a final end-to-end test, set both DEBUG flags to NO.

The solution provided includes the store class (SimpleStore) and several support classes.  I have provided the class in a project that demonstrates how the GUI (controller) classes should ONLY perform "controller" operations, while non-GUI code implements "model" logic.  One of my pet peeves with iPhone developers is that they all pay a lot of lip service to the MVC pattern while breaking most of it's rules by putting "model" logic into "controller" classes.  This isn't a stylistic issue,  this is a testability issue.  But then again, most iPhone developers don't test their code rigorously  :)

So let's make that our first Rule of Evolved Software.
Rule #1:  Keep "model" logic out of "controller" classes.


A related idea is the concept of modularity and abstraction.  If you think of classes as representing inter-related processing algorithms, one thing you want to do is try to keep the classes as tightly focused on solving a specific problem as you can.  This is represented in the provided solution in the following way:

The SimpleStore class wraps around the StoreKit and simplifies the programming API as far as the rest of the app is concerned.  Ideally we want to write this code once and then NEVER modify it again!  Because once we have tested it, and have some confidence that it works, any changes to the processing logic may introduce bugs.  So this code doesn't know anything about the app it is working in -- it is a generic solution to the difficulties associated with working with StoreKit.

The PurchaseScreen (which is a view controller) handles the GUI.  This, of course, is application specific.

But there is a little bit of model code that isn't Controller related, but isn't generic either.  This is model code which is specific to this application.  It shouldn't go into SimpleStore (because we NEVER TOUCH IT, right?) and it shouldn't go into PurchaseScreen because it isn't controller code.  Solution?  Purchase class.    Yes, the class is tiny.  Yes, the class doesn't do much.  That isn't the point.  It needs to be there.  If you "optimize" it away by moving the code into the other classes, you are learning bad habits that you will eventually regret, if you ever graduate to writing bigger systems and working with other people to develop and maintain code.

I'm only going to talk about one other thing, and that's the code to detect when we are running in the simulator.  If you google for solutions to this problem, you will find a lot of suggestions that you use preprocessor macros to determine the runtime environment, such as this:

#if TARGET_IPHONE_SIMULATOR
// This code will only appear for the simulator
#else
// This code will only appear for a real device
#endif


I'm not going to go into all the reasons to avoid #ifdef and #if, and please don't flame me just because you may have a specific situation where it is justified.  I will simply state that introducing conditional code compilation into your project will usually make it more difficult to maintain in the long run.  Avoid it whenever you can.  Searching for alternative solutions is time well spent.

In this case, there is a perfectly valid alternative approach which I've encapsulated in the RuntimeInfo class:  check to see if the name of the runtime environment includes the string "simulator".

Why is this better?  Well, what happens if you decide to make an iPad version?  Does the conditional compilation solution, as written, produce a simulator version if you target an iPad device then run it in the simulator?  I don't think it will.  At the expense of just a few more minutes we have written runtime code that should be correct, regardless of your target and regardless of your device.  We have written code which is far more likely to handle new situations better.

That's what I'm talking about.  :)

Terry

Here is a link to the project:  http://simplestoreevolved.googlecode.com/files/SimpleStoreEvolved.zip
Terry On
Welcome to my blog.

I am a 20+ year software development professional.  A veteran, by any standard I know of.  Over the course of my career I have worked on many fascinating projects including the ground software for GPS (perhaps you've heard of it?), an early version for the International Space Station command and control software, an anti-ICBM Star Wars "space shield" system, and flight critical components for 757 aircraft.  I have worked on projects individually, but I have also managed 50+ people working in cross-functional teams to deliver suites of software projects.

I have written code which, if it fails, could result in loss of human life.  This requires a set of skills, techniques, and mindset which you might not otherwise develop.

When I started developing code for the iPhone I noticed a distinct lack of reusable, sophisticated components.  Part of this is due to the nature of the device, and part of it is due to the nature of the SDK.

I do NOT think that it is due to the lack of knowledge or expertise of the huge community of developers that exists for the iPhone.  Time and time again I have been impressed by their generosity, cleverness, and general programming skills.

But I routinely see people advocating approaches or providing example code which is horribly written from my point of view:  it may solve the immediate problem, but can't be re-used.  Or it solves an obvious problem while introducing a subtle maintenance issue.  Or it requires re-compilation to change it's behavior.

While there isn't anything overtly wrong with that, I want to introduce people to code which takes a longer view of the software lifecycle.  Code which understands that it needs to be tested, deployed, and maintained.  Code which makes the developers' lives easier.  Code which has evolved from the approaches taken in the 60's, 70's, 80's, and 90's.

I hope to use this blog to introduce people to my mindset.  I hope you find it useful.  I hope you find bugs in my software that have escaped me.  I hope we learn together to produce apps that do more, and require less effort.

Let the coding begin!

Terry