Sunrise Alarm

Sunrise Alarm was something I wrote last summer to get me out of bed in time to reach the peak of my mountain bike ride before the sun got too high. It's basically a standard alarm clock app, except that the alarm time depends on the current day of the year, and the location of the phone. The idea is that as the day length changes, you still end up getting on your bike at the right time to avoid the brutal heat of southern california summers. The app binary is in the app store and the source code is available as an xcode project.

In iOS 5, you have to explicitly enable notifications for the app in the phone's notification settings for the alarms to work when the app is not launched, or is in the background.

Technical Challenges

The app has to know the sunrise times for the current week based on the phone's location. It also has to know the current weather conditions. And because it's a universal app, it has to deal with two user interfaces, one for each screen size (iPhone and iPad). Since it runs on the iPad it also has to handle all screen orientations. It also has to look like an old school LED clock. And the date, the abbreviations of the days of the week, and the weather conditions should be in the user's native language. The alarm should go off if the app is running, if the app is suspended in the background, and if the app is not even launched. Settings should be done in-app. Finally, there should be a bunch of interesting sounds that the user can pick from.

User Interface

The user interface is straightfoward. There are two main views, a Clock view and a Settings view, and the user switches between them with a Tab Bar. This UI stuff is straightfoward and, aside from the Clock class, it gets rid of the bottom half of the architecture diagram.

Solutions

The app gets its location using iOS's location manager. The app turns on the location service (which draws power), it requests a location update, and some time later it gets an answer. Then it turns off the location service to save power.

The app uses Yahoo's free weather api to get the current weather. This involves sending out a series of (2) http requests to yahoo's servers and parsing the resulting information, which arrives some time later as xml data.

The universal app support is done in the usual way, by subclassing from base view controller classes for device specific code. Mainly this just involves overriding viewDidLoad (). Separate xib files are made for the two kinds of devices which iOS loads automatically. There's still one occurence of a runtime test for device type out of expediency. Roation is done by having a portrait and a landscape view which is swapped out when the orientation changes, iOS aniimates this so it looks like it's one view that's re-arranging its components to fit the new size. This is a simple way to not have to deal with a mass of text fields inside a view re-arranging themselves "automatically" one orientation changes.

The (not very good looking) LED display is done by installing a non-custom LED font, stolen off the internet somewhere. iOS lets you include your own fonts in an app bundle and load them at runtime.

The internationalization of dates and weekday names is provided by the right calls to iOS's date formatting framework. Yahoo also is smart enough to return a location specific string for its weather status.

All alarms are scheduled through iOS's local notification framework. This solves the problem of the alarm having to go off no matter what state the app is in.

Settings are done with a singleton class that reads and writes itself as an archive too and from flash. For something simple, using Core Data to do persistent object store is overkill. Aside from that, I haven't taken the time to play with Core Data, other than reading the spec.

I took sounds from freesound.org, searching for annoying sounds that were just under 30 seconds long. If an alarm goes off while the app is not launched and not suspended in the background, the sound is done by the iOS notification service (when it brings up an alert allowing the user to launch the app. The limit to how long a sound can be in these notificaions is 30 seconds.

The Only Tricky Thing

The only thing tricky aspect of the app is dealing with the asynchronicity. The app has to make requests for the current location, and also it has to make http requests for the weather, and it can't block waiting for the results. Also to keep the clock ticking the app has to run a task in a timer that goes off every second. For a simple app like this it's not worth the complexity to use threads. So, while it's possible to do the http requests in a separate thread that blocks, it gets complicated when the results come back, especially when the user interface has to be touched, since all calls to UIKit have to be on the main thread. The same is true for using location services, which are asynchronous as well. The simple way to do this is to use the main thread's runloop to do what it's designed for. Nonblocking HTTP requests have their replies come back in delegate calls on the main thread, and the same is true with the location service. In the architecture diagram (below) the use of delegate methods is shown with three dots and the blue text "delegate callback" or an abbreviation for that.

A related complication is making sure the app is always setting its alarms correctly. Whenever the user changes the app settings (which, among other things, lets him pick what days of the week, and what time offests from sunset he wants to wake up at) the app has to reset its alarms. Also whenever the device is launched (perhaps in a new location) its alarm state needs to be refreshed. Finally, whenever an alarm goes off, it has to be rescheduled, and this depends on where the device is on the planet. To make it easy for the app to reset itself, the whole process of getting the current location and rescheduling alarms is triggered by an updateLocation request. That starts a chain of other requests that ends in the alarms being reset correctly. In the architecture diagram this is shown on the left under the blue label, "refresh state". The delegate methods shown by the dots there are the same ones on the right side of the diagram where the objects that actually do the work are shown

These objects to the right of the AppDelegate in the diagram are helper objects that report their answers asynchronously back to the appDelegate through various delegate methods.

Architecture

Below is a diagram of the overall app architecture. Everything below the dotted line is standard iOS controllers (and views) doing the UI. In the MVC pattern, the app's model is the data kept by the Settings object. This data is settings but also the last known location of the device. This is done for a bit of robustness, to handle the case where the user might have disabled the location service after having used the app once. Without at least some valid location, the app is useless.

The rest of this document will describe some of the app's implementation.

At the very end of this document there's some code snippets from a totally different app (Maze Solver), as an example of some fun, pure obj-c algorithmic code, for variety that I can't get out of examples from Sunrise Alarm.

Sunrise_AlarmAppDelegate

Sunrise_AlarmAppDelegate is the top-level object which encapsulates the entire app. It has member helper objects that do most of the work: "weather" to get the current weather conditions, "alarm" to play alarm sounds, "scheduler" to schedule alarms, "locationManager" to get the device's current location. It also is the root of the app's user interface tree, by means of the "window" and "tabBarController" outlets, which are set from the main .xib file when the app launches. Sunrise_AlarmAppDelegate exposes some methods to hide and show the tab bar. Finally, it exposes a method that triggers a refresh of the entire app state, "updateLocation". This refresh process involves a chain of asynchronous requests and delegate callbacks and is shown on the left hand side of the architecture diagram above. There is a common entry point to this refresh process which Sunrise_AlarmAppDelegate protects with the gettingWeather BOOL. Since the app is single threaded, by design, we can use an ordinary BOOL as a kind of a mutex.

Whenever some part of the app decides that the alarm settings are invalid, it can rely on this method (which is effectively a global function call) to do the right thing. This helps remove some complexity in the app implementation.

There is still a race condition, because getting a new location happens asynchronously, and after a delay, and so the app might be terminated by the user before updateLocation is done. This is dealt with by tentitively rescheduling alarms based on the previous known device location, and only then trying to get a new location. If the app is going to be put into the background while the location update is pending, the app just cancels the location update request before it suspends. This decision reflects a tradeoff in the complexity of executing background code and the likelihood that the user has shifted timezones between alarms and has his app settings and has terminates the app in what probably has to be way under 1 second. It is technically bad code though, and the right way to have done it would have been to have spent 2 minutes looking up how iOS does manage this case, and having done the right thing. (Turns out, it is easy, of course).

The Settings Object

Settings is important because, in this little app, it represents the "model" component of the MVC design pattern. It also is a singleton class, which means only one instance of it can ever exist, andany part of the app can get a reference to this object through a class method. This is a clean way of making Settings a global variable. The Settings objecet keeps track of the current app settings, as well as the device's last known location. It also provides some methods to help map settings to human readable strings for drawing the user interface with. So, the settings property "time" is an index into the array returned by "getTimeStrings". urlForSound:index looks up the sound .wav file for a particular sound index setting. archive: dumps the object to flash for persistent store. (This is called by Sunrise_AlarmAppDelegate just before the app is terminated or suspended.)

Settings.h

Making Settings a singleton class involves overriding the standard allocators and deallocaters for the class to always return the same object and no-op, respectively, and implementing a class method, "sharedSettings" so that any part of the app code can get the object as if it were a global variable. Here's what sharedSettings looks like. If it's not been called before, an settings object is allocated and initialized from data on flash, and a reference to this object is stored in a static global. On subsequent calls this rererence is returned. Retain and release on this object do nothing because those methods have been overridden. This idiom is the standard way of doing Singleton classes in objc-/cocoa, and Apple's own frameworks do this. For instance UIApplication's "sharedApplication" class method always returns a reference to the running application object.

Settings.m

The human readable strings that "Settings" returns are not internationalized. This is bad code. For instance,

The fix is to use string resource files and the NSLocalizedString() macro to get strings in the user's language.

HTTPFetcher

HTTPFetcher is a class that adds fault tolerance to the standard iOS way of making HTTP requests. (NSURLConnection). It deals with timeouts and assembling multiple replies into one chunk of data, and optionally translating that data into a string. It also allows requests to be cleanly cancelled.

This class is interesting because it uses delegation instead of subclassing to extend NSURLConnection. Many iOS (Cocoa) frameworks are designed to be extended through delegation rather than inheritence, this dates back from Cocoa's smalltalk roots. Generally, built in user interfaces classes (UIKit classes) are designed to be subclassed, but classes that do more process oriented stuff, for instance NSURLConnection are often better off extended through delegation. HTTPFetcher lets the programmer re-use about 200 lines of intricate and yet tedious bookkeeping code that arise whenever you use NSURLConnection. HTTPFetcher can be run on the main thread or on a separate thread, depending on what the programmer wants. In this simple app, everything is done on the main thread.

HTTPFetcher.h

HTTPFetcher.m

YahooWeather

The YahooWeather class deals with fetching the current weather conditions from Yahoo's servers. It hides the complexity of making the series of HTTP requests and waiting for the answers and sifting through the last XML reply for the weather string. This hiding of complexity is done through delegation. The object calling YahooWeather's goWithLatitude:longitude() method has a callback method, weather:gotWeather(), which YahooWeather calls when it has the new weather string. On error the string is nil.

YahooWeather is an example of how the app uses delegation all over the place, to deal with various asynchronous requests. Getting the latest location, reacting to notifications to actually ring the alarms, and at a lower level making HTTP requests all use delegation to simplify things.

YahooWeather uses an instance of HTTPFetcher to talk to the Yahoo servers.

YahooWeather.h

Here's an example of how the appDelegate uses its YahooWeather member object, "weather" to update the clockface with the latest weather conditions.

Sunrise_AlarmAppDelegate.m

Scheduler

Scheduler is a fun little class that uses location, current app settings, and astronomy math to reschedule alarms. It's the heart and soul of the app. Scheduler exports methods to reschedule alarms, to return a string representing dawn of the current day, and to schedule a short notification (for snoozing). Sunrise_AlarmAppDelegate uses this helper class to deal with all the app's scheduling needs.

Scheduler.h

The implementation makes heavy use of iOS's date formatting and conversion classes. Note the bad code that lacks internationalization of user interface strings when the notifications are being created. The fix was mentioned above. The astronomy math comes from an open source file called called sunriseset.c, which dates back to 1989. Everyone on the planet uses this file to do this kind of astronomy (even sites computing dawn). It's ancient lore.

Scheduler.m

Alarm

Actually playing the alarm sounds is done by a helper object called Alarm. This object hides the details of how to play short sounds on iOS (using AVAudioPlayer) from the rest of the app. Sunrise_AlarmAppDelegate can simply call its alerm object's play and stop methods to do the right thing.

Alarm.h

The implementation uses two different sound iOS frameworks, to either vibrate or play a sound, depending on the user's settings. It also does a bit of work to make sure the sound sample loops, or that the vibrate is continuous. In this app, the alarm will go on forever, unless the user presses snooze or stop.

Alarm.m

Sunrise_AlarmAppDelegate Again

Sunrise_AlarmAppDelegate is the entry point for the app. It sets up the initial app user interface, it creates all of the app's helper objects (location, alarm, scheduler, weather, etc), and it deals with notification and app termination events. It also implements a modal view for the snooze screen, which slides up when an alarm goes off, to give the user a chance to snooze, or shut the alarm off.

The snooze implementation is bad design, because it obscures the clockface. So when the alarm goes off, the user has to press the snooze or stop button to see the what time it is, and how cold it is outside. The tradeoff was having giant buttons versus making the snooze view transparent or not screen-sized. In practice the user can always use snooze, but it's not perfect. Having a small display of the time and the weather intergrated into the snooze modal view might have been the best solution.

Here is the main entry point for the app, where the app is set up:

Sunrise_AlarmAppDelegate.m

There are a set of AppDelegate delegate methods that are called when the app moves into and out of the background and terminated states. Sunrise_AlarmAppDelegate does things like cancel an existing location update request (this was the race condition mentioned above), save app settings to flash, and trigger a refresh of the app state when the app is brought out of the background.
Here is the YahooWeather delegate callback that gets called when a new weather string is got off the internet. Sunrise_AlarmAppDelegate just uses the string to update the clock face.

And here's the delegate callback for location updates finishing. AppDelegate uses the new location to reschedule alarms and to request a weather update if one isn't already pending. This makes it so that whenever the app is brought to the foreground, it gets a new weather status string without having to wait for the weather update timeout to fire (in the background the app refreshes the weather string once every minute or something. This use of the network can be turned off in settings.)

There's a second delegate callback that's called whenever the user enables location services, this gives the app a chance to get a location when it previously was not allowed to.

Finally here are some utility methods Sunrise_AlarmAppDelegate exports so other parts of the app can request location updates or toggle the tabBar and so on. Making these methods essentially global function calls is done with a helper singleton class called Globals. This way of doing things is not quite right, but I haven't found out a cleaner solution yet. One reason it's nice to work with other developers, you can share ideas.

UI Implementation Detail: ClockViewController

Supporting two different kinds of devices (iPhone and iPad), in this app, is just a matter of dealing with the different screen sizes. The user interface design itself doesn't change. To deal with this we write a view controller subclass that does most of the work -- loads the view nib, handles events, but we subclass the controller for each device overriding what we need to do devic specific things. An example is ClockViewController and its subclasses, ClockViewController_iPhone and ClockViewController_iPad. The only device specific code is setting the font sizes, which is done by overridding viewDidLoad ().

ClockViewController.h

ClockViewController_iPad.m
ClockViewController_iPad.m
The bulk of the other behavior done by all ClockViewControllers is dealing with device orientation changes and showing a hidden tabBar if the user presses the screen. Supporting all orientations is done by having a pair of totally independent views, one for landscape and one for portrait, which are swapped when needed. iOS automatically animates this when the viewController's view property is modifed. This simple way of not even trying to have a single view modify its subviews to react to orientation changes simplies the UI code a lot. The ClockViewController baseclass is also responsible for responding to Settings changes. It knows whenever it is being made the active tabBar view and reacts accordingly. This is bad design because it's not clear to the user when his changed settings take effect. The right way of doing this would be to handle changes as the user makes them in settings.

ClockViewController.m

Maze Solver

This is a little toy app too useless to put in the app store. It is supposed to show off my computer science skills. The user draws a maze on a piece of paper or a whiteboard, takes a picture of the maze with his iPhone, and the app solves the maze. By drawing a green line from a start point to an end point, which filled in blobs the user draw on the maze. There are lots of little computer sciencey parts to this, color to grayscale conversion, noise removal, blob detection, and then the actual maze solving. The app is ok, except for the blob detection, the brute force algorithm I devised for that runs at n^2 log(n) and aside from that is too fragile to be any good even on an infinitely fast processor.

The source code for the app is here: http://www.davidfinucane.com/MazeSolverExample.zip.

Here's what a solution looks like, with the colored in squares showing what the start / stop blobs look like.

And a closeup of the lower right corner showing some remaining noise from the color to grayscale conversion.


Noise Removal

When the color image is converted to gray scale, some pixels end up mapped to black instead of white. They show up as little clusters of connected black pixels. Maze Solver removes these pixels by scanning the bitmap for the black and white version of the image for little clumps of black pixels, little islands. Once it finds an island (say, any clump containing fewer than 5 pixels) it erases them in the bitmap. The implementation uses a little recursive depth first search that gets called on each black pixel as the bitmap is being scanned, and the search is done in 2 passes, once to detect a clump, and a second time to erase it.

MSBitmap.m

Closures in Objective-C!!

One of the coolest new-ish features to objective-c (and eventually to the c++ programming language) is support for closures. This lets you write code with blocks that you can use for doing stuff on threads or instead of registering callbacks. It lets you write elegant code in some cases, reminiscent of javascript. Here's an example of how Maze Solver runs the solving method on a new thread, waits for it to terminate, and updates the user interface based on the answer. This method is called straight from the action wired to the "solve" button in the app.

Notice how the cascading ^{'s look like successive calls to function (){ .... in javascript (or any other language with closures).

RootViewController.m

Terrible Blob Detection Algorithm

This takes just a few seconds to explain on a whiteboard. It's horribly brute-force. And I'm out of time: time to go biking...