Andrew Romanenco.com

Update application content in background on IPhone

Purpose

Many mobile applications display content to their users. This content can be displayed while

user is online, offline or both. Online mode is trivial - on UI event, application goes to

server, receives data and displays it on screen. Offline mode is much more interesting: user

can read documents everywhere and anytime. Also ,offline mode offers much more

advantages - if content is large (like PDF files), having it on client side decreases response

time. But these options bring some difficulties - application must be able to update offline

content, and it must do it in user friendly way, let's discuss this and make some kind of

"software requirements".

Topics covered in source code

- accessing data from web server

- parsing xml

- accessing file system

- multi threading

SRS (Software requirements)

Our application must display text files to user in offline mode. So it should check for new

files on each start and download them to device. Of course, update activity must not block

user interface, and user must be able to work with files, which are already available.

Update process contains two steps:

1. Download xml file with list of PDF files (each item contains name and url to download

data from)

2. Download each file, using URLs from step one

3. Both steps will run in separate thread

 

Architecture and design

First of all, application will have a singleton object - UpdateManager - it will control entire

process, by delegating actual actions to separate objects "Updaters". So we need two

updaters for now: one for xml list reading and parsing, and one for files download. Later, we

can add additional updaters, and this is good reason for declaring single facade for them all.

This facade will declare at least one method: start, so UpdateManager will call start each

updater, one by one.

We already know, that each updater uses networking, and it will work in asynchronous

mode. Asynchronous mode requires us to continue UpdateManager's activity, when

networking job is done.

Declare two protocols:

@protocol UpdateManagerProtocol <NSObject>

-(void)next;

@end


@protocol UpdaterProtocol <NSObject>

-(void)startUpdate:(id <UpdateManagerProtocol>) manager;

@end

UpdateManagerProtocol declares single method: continue - each updater must call this

method, when job is done.

UpdaterProtocol also declares single method. When it is called, updater must do update.

Single parameter is reference to UpdateManager, to call "next" method, when job is done.

Here is class diagram for our classes

We already know, that each updater will start asynchronous network connection, to reduce

code duplication we can add additional abstract class (this class is not presented on

diagram)

All updaters work in the same manner:

Job to be done by XMLListUpdater:

1. Read xml file from web server to memory

2. Parse xml

3. Add each file item to queue (DataModel)

Job to be done by FileUpdater:

1. Read next item from queue

2. Check, if file already exists on drive

3. Download file

4. Repeat, while queue is not empty

Implementation

Let's start implementation without any thoughts about multi threading.

UpdateManager.h declares static method to run update process, as well as storage for steps

to be done. These steps are created in class constructor (init) - each class implements

UpdaterProtocol.

Static methods has simple logic: initialize one instance and delegate activity to first updater.

UpdateManager implements UpdateManagerProtocol, and method "next" is called right after

active updater finishes work. "next" starts next updater, if it exists.

As every updater accesses network, it has point to move common code to single class -

NetworkClient. It implements UpdaterProtocol and implements method to start

asynchronous network connection(startNetworkCall).

Out first, concrete updater is XMLFileUpdater. On start, it run network connection to

predefined url address, it also has memory buffer for xml data. When connection finishes,

XMLListUpdater creates xml parse and process buffered data. Parsing is very simple - it

caches file id and url tags, and put these data to queue (implemented by DataModel class).

Right after parsing is done, UpdaterManager's "next" method is called.

Second updater is FilesUpdater - it must go through the queue, and download missed files.

It looks similar to previous updater, but does not use memory for data caching, and writes it

directly to drive. Only one note: when file is downloaded, thread is stopped for 10 seconds -

to emulate high payload.

Now we can run UpdateManager when main view is loaded, and application will synchronize

content.

View has only one button, without any action attached. If you will try to touch it, while

updating - you will see that interface is blocked. But we can solve this by running all update

actions in separate thread.

Upgrading to separate thread

As we already have all logic, all that we have to do is to create new thread, tune it and

leave working.

Let's add new method to UpdateManager - startInThread. It has very simple

implementation:

1. Create NSAutoReleasePool instance

2. Run update process

3. Start RunLoop

4. Release pool

NSAutoRelease pool is required for every new thread, to handle automatic memory

management. You will have may workings without it.

RunLoop is much more interesting item. If you will comment out RunLoop code and start

application, you will get message about starting network connection, but you will never get

any events, like data arrived, connection finished and so on. The problem is in thread - it

finishes when method "startInThread" is done. That's why the code has to run it's own

activity, to continue working.

Now we have all code ready, and we can move UpdateManager start to main.h, instead of

view loading.

Notes about sample source code

Source code has conditional compilation directives: UpdateManager.h -

WORK_IN_SEPARATE_THREAD. If it is set to zero, then no new thread is created, and you

will see UI problems, because of blocking. If it is set to one - new thread is run.

Download sources here: SF.net 





         romanenco.com 2008