<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-259176237881102296</id><updated>2010-04-16T07:14:13.689-07:00</updated><title type='text'>Pronoid Android</title><subtitle type='html'></subtitle><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default?start-index=26&amp;max-results=25'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.rezendi.com/pa/atom.xml'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>31</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-5323615496616533005</id><published>2010-04-16T07:14:00.001-07:00</published><updated>2010-04-16T07:14:13.715-07:00</updated><title type='text'>This blog has moved</title><content type='html'>&lt;br /&gt;       This blog is now located at http://pa.rezendi.com/.&lt;br /&gt;       You will be automatically redirected in 30 seconds, or you may click &lt;a href='http://pa.rezendi.com/'&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;       For feed subscribers, please update your feed subscriptions to&lt;br /&gt;       http://pa.rezendi.com/feeds/posts/default.&lt;br /&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-5323615496616533005?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/5323615496616533005/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/04/this-blog-has-moved.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5323615496616533005'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5323615496616533005'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/04/this-blog-has-moved.html' title='This blog has moved'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-5833802590989928207</id><published>2010-04-16T06:52:00.001-07:00</published><updated>2010-04-16T07:04:52.208-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='currentController'/><category scheme='http://www.blogger.com/atom/ns#' term='synchronization'/><category scheme='http://www.blogger.com/atom/ns#' term='maps'/><category scheme='http://www.blogger.com/atom/ns#' term='performSelector:'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='selector'/><category scheme='http://www.blogger.com/atom/ns#' term='UINavigationController'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='MapView'/><category scheme='http://www.blogger.com/atom/ns#' term='selectors'/><category scheme='http://www.blogger.com/atom/ns#' term='NSMutableSet'/><category scheme='http://www.blogger.com/atom/ns#' term='sets'/><category scheme='http://www.blogger.com/atom/ns#' term='threading'/><title type='text'>My love/hate relationship with the iPhone SDK</title><content type='html'>There is a lot to hate about the iPhone SDK. But there's also a lot to love.&lt;br /&gt;&lt;br /&gt;My app iTravel goes out and gets Wikitravel pages, and then gets subpages in the background. (eg if you ask for "New York City", it'll also go out and get "Manhattan", "Upper West Side", etc.) It also plots listings (sights, restaurants, etc.) for all these pages on a map. I wanted the background thread that was loading this data to automatically a new page's listings to the map, if the viewer was using one; so they can go to "New York City", go to the map, and then watch Manhattan slowly get barnacled by map annotations, one neighbourhood at a time.&lt;br /&gt;&lt;br /&gt;I thought this was going to be difficult. I couldn't have been more wrong. Here's the background-thread code, in its entirety:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void) refreshMapIfActive {&lt;br /&gt; UIApplication *app = [UIApplication sharedApplication];&lt;br /&gt; iTravelRightAppDelegate *appDelegate = app.delegate;&lt;br /&gt; UINavigationController *controller = [appDelegate navigationController];&lt;br /&gt; NSArray *viewControllers = [controller viewControllers];&lt;br /&gt; UIViewController *currentController = [viewControllers lastObject];&lt;br /&gt; if ([currentController class] == [MapViewController class])&lt;br /&gt;  [currentController performSelectorOnMainThread:@selector(showAnnotations:) withObject:nil waitUntilDone:NO];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;I'd like to do this in Android, too...but a) their map implementation works a lot slower, b) I don't think there even &lt;i&gt;is&lt;/i&gt; a method to get the currently active Activity.&lt;br /&gt;&lt;br /&gt;Now, the "showAnnotations:" method within MapViewController is obviously trickier. For one thing, it's synchronized, lest the user try to filter a map just when a background thread is adding listings - not really a UI issue, since this usually takes all of 1-2 seconds:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void)doShowAnnotations:(NSArray*)annotationsToShow {&lt;br /&gt; //autorelease pool&lt;br /&gt; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];&lt;br /&gt; &lt;br /&gt; @synchronized (self) {&lt;br /&gt;  @try {&lt;br /&gt;   NSArray *wikiPageMarkers = [WikiPage getPageMarkersWithin:mapView.region];&lt;br /&gt;   &lt;br /&gt;   BOOL added=NO;&lt;br /&gt;   double pageLevel = [Settings getMapPageLevel];&lt;br /&gt;   BOOL abovePageLevel = mapView.region.span.longitudeDelta &gt; pageLevel || mapView.region.span.latitudeDelta &gt; pageLevel;&lt;br /&gt;&lt;br /&gt;   if (abovePageLevel &amp;&amp; [mapView.annotations count] &gt; [wikiPageMarkers count]) { //clear away low-level annotations&lt;br /&gt;    [mapView performSelectorOnMainThread:@selector(removeAnnotations:) withObject:[annotations allObjects] waitUntilDone:YES];&lt;br /&gt;    [annotations removeAllObjects];&lt;br /&gt;   }&lt;br /&gt;   else if (!abovePageLevel)&lt;br /&gt;   {&lt;br /&gt;    UITabBarItem *selected = [self.tabBar selectedItem];&lt;br /&gt;    if (selected!=nil) {&lt;br /&gt;     //first, remove all annotations that don't fit the selection&lt;br /&gt;     NSMutableArray *toRemove = [NSMutableArray arrayWithCapacity:[mapView.annotations count]];&lt;br /&gt;     for (NSObject *annotation in mapView.annotations) {&lt;br /&gt;      if ([annotation class] == [Listing class]) {&lt;br /&gt;       Listing *listing = (Listing*) annotation;&lt;br /&gt;       if (!(selected.tag==MY &amp;&amp; [listing isInMyListings] || selected.tag==[listing.category intValue]))&lt;br /&gt;        [toRemove addObject:listing];&lt;br /&gt;      }&lt;br /&gt;     }&lt;br /&gt;     [mapView performSelectorOnMainThread:@selector(removeAnnotations:) withObject:toRemove waitUntilDone:YES];&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    if (annotationsToShow==nil) { //if we don't have a specific request, get all the listings within the map's region&lt;br /&gt;     NSNumber *category = selected==nil ? nil : [NSNumber numberWithInt:selected.tag];&lt;br /&gt;     annotationsToShow = [ListingManager getListingsWithin:mapView.region forCategory:category];&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    //the following is incredibly messy because NSSet and NSMutableSet are unusable for our purposes.&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;What follows is one of the reasons I hate the SDK. Basically, I want to do some fairly basic set arithmetic to ensure that we remove undesired annotations from the map (but keep them in our local "annotations" set in case we need to add them again) and add new ones that are desired. Because the "-unionSet:" etc. methods on NSMutableSet don't work at all like you'd expect, though, I basically have to do that by hand. I'll skip over that messy part to the good stuff:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;     NSMutableArray *thereNotRequested = [NSMutableArray arrayWithArray:mapView.annotations];&lt;br /&gt;     for (annotation in mapView.annotations) {&lt;br /&gt;      if ([requested objectForKey:[annotation title]] != nil)&lt;br /&gt;       [thereNotRequested removeObject:annotation];&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     [mapView performSelectorOnMainThread:@selector(removeAnnotations:) withObject:thereNotRequested waitUntilDone:YES];&lt;br /&gt;&lt;br /&gt;    if ([arrayToAdd count]&gt;0) {&lt;br /&gt;     [mapView performSelectorOnMainThread:@selector(addAnnotations:) withObject:arrayToAdd waitUntilDone:YES];&lt;br /&gt;     added=YES;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;   if (!added)&lt;br /&gt;    [self performSelectorOnMainThread:@selector(activityDone) withObject:nil waitUntilDone:NO];&lt;br /&gt;  }&lt;br /&gt;  @catch(NSException *exception) {&lt;br /&gt;   [Util doLog:[NSString stringWithFormat:@"Warning: adding annotations to map view failed: %@", [exception reason]]];&lt;br /&gt;   [self performSelectorOnMainThread:@selector(activityDone) withObject:nil waitUntilDone:NO];&lt;br /&gt;  }&lt;br /&gt;  @finally {&lt;br /&gt;   [pool release];&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-5833802590989928207?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/5833802590989928207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/04/my-lovehate-relationship-with-iphone.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5833802590989928207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5833802590989928207'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/04/my-lovehate-relationship-with-iphone.html' title='My love/hate relationship with the iPhone SDK'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-7333431738795067216</id><published>2010-04-06T19:28:00.000-07:00</published><updated>2010-04-06T19:58:59.220-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DB'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLite'/><category scheme='http://www.blogger.com/atom/ns#' term='openOrCreateDatabase'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLiteDatabase'/><category scheme='http://www.blogger.com/atom/ns#' term='Context'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='SDK'/><category scheme='http://www.blogger.com/atom/ns#' term='SD'/><category scheme='http://www.blogger.com/atom/ns#' term='getDatabasePath'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLiteOpenHelper'/><category scheme='http://www.blogger.com/atom/ns#' term='DBHelper'/><category scheme='http://www.blogger.com/atom/ns#' term='card'/><title type='text'>SQLite on Android</title><content type='html'>So a &lt;a href="http://www.cyrket.com/p/android/com.rezendi.itravel/"&gt;commenter&lt;/a&gt; on Android Market suggested I allow users store my app data on their SD card rather than phone memory. Great idea, I thought, and promptly went Googling to look for some example code. Alas, there wasn't any. So I wrote my own; and here it is for those who find themselves in a similar situation.&lt;br /&gt;&lt;br /&gt;The basic Android model is for all Activity classes to have an associated DBHelper class. I don't like this at all; you waste all kinds of time writing and instantiating and opening and closing your DBHelpers, for the sake of (in my app) maybe twenty different database calls. So instead I went and created a singleton DB object. Here's the static code:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;public class DB {&lt;br /&gt; private static DB instance;&lt;br /&gt;    SQLiteDatabase mDB;&lt;br /&gt;    DatabaseHelper mDbHelper;&lt;br /&gt;    final Context mCtx;&lt;br /&gt;&lt;br /&gt;    public static DB GetFor(Context context) {&lt;br /&gt;        if (instance==null)&lt;br /&gt;            instance = new DB(context);&lt;br /&gt;        if (!instance.isOpen())&lt;br /&gt;            instance.open();&lt;br /&gt;        return instance;&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public static void Close() {&lt;br /&gt;         if (instance!=null &amp;&amp; instance.isOpen())&lt;br /&gt;             instance.close();&lt;br /&gt;        instance=null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Database creation sql statement&lt;br /&gt;     */&lt;br /&gt;    static final String MESSAGE_CREATE =&lt;br /&gt;        "create table Message (_id integer primary key autoincrement, "&lt;br /&gt;   + "content text not null, dateCreated datetime not null, dateViewed datetime, "&lt;br /&gt;   + "messageTitle text, messageType integer, sender text, recipient text); ";&lt;br /&gt;&lt;br /&gt;//  [...other table definitions go here...]&lt;br /&gt;&lt;br /&gt;    static final String DATABASE_NAME = "iTravel";&lt;br /&gt;    static final int DATABASE_VERSION = 1;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;(I call DB.Close() in the onDestroy() method of the app's Activities. Now, if you had multiple threads writing to the database simultaneously, this approach would probably get pretty messy in a hurry; fortunately, I don't.)&lt;br /&gt;&lt;br /&gt;The Android SDK also includes a SQLiteOpenHelper object, which basically takes a Context object and returns a writeable database. I was a little irritated by the need to pass in a Context - it means you have a Context on hand to access the database at all - but presumed it was just necessary for some mysterious reason.&lt;br /&gt;&lt;br /&gt;Not so mysterious at all. The bad news is, if you want your database to live on your phone's SD card, you can't use the SQLiteOpenHelper. The good news is, it's easy to write an SD-card-compatible variant, one that doesn't require any Context at all:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;     static class DatabaseHelper{&lt;br /&gt;     private SQLiteDatabase db;&lt;br /&gt;     private Context mCtx;&lt;br /&gt;&lt;br /&gt;     private DatabaseHelper(Context context) {&lt;br /&gt;        mCtx=context;&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     public void open() {&lt;br /&gt;         File dbDir=null, dbFile=null;&lt;br /&gt;         if (Settings.DoSDDB() &amp;&amp; Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {&lt;br /&gt;             dbDir = Environment.getExternalStorageDirectory();&lt;br /&gt;             dbFile = new File(dbDir, "iTravel.sqlite");&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;             dbFile = mCtx.getDatabasePath("iTravel");&lt;br /&gt;&lt;br /&gt;         if (dbFile.exists()) {&lt;br /&gt;             Log.i("SQLiteHelper", "Opening database at "+dbFile);&lt;br /&gt;             db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);&lt;br /&gt;             if (DATABASE_VERSION &gt; db.getVersion())&lt;br /&gt;                 upgrade();&lt;br /&gt;          }&lt;br /&gt;          else {&lt;br /&gt;              Log.i("SQLiteHelper", "Creating database at "+dbFile);&lt;br /&gt;              db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);&lt;br /&gt;              create();&lt;br /&gt;          }         &lt;br /&gt;     }&lt;br /&gt;        &lt;br /&gt;     public void close() {&lt;br /&gt;         db.close();&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     public void create() {&lt;br /&gt;         Log.w(""+this, "Creating Database "+db.getPath());&lt;br /&gt;         db.execSQL(MESSAGE_CREATE);&lt;br /&gt;//          ...other tables go here&lt;br /&gt;         db.setVersion(DATABASE_VERSION);&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     public void upgrade() {&lt;br /&gt;         Log.w(""+this, "Upgrading database "+db.getPath() +" from version " + db.getVersion() + " to "&lt;br /&gt;                + DATABASE_VERSION + ", which will destroy all old data");&lt;br /&gt;         db.execSQL("DROP TABLE IF EXISTS Message");&lt;br /&gt;//       ...other tables go here&lt;br /&gt;         create();&lt;br /&gt;     }&lt;br /&gt;&lt;br /&gt;     public SQLiteDatabase getWritableDatabase() {&lt;br /&gt;        if (db==null)&lt;br /&gt;            open();&lt;br /&gt;        return db;&lt;br /&gt;     }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Turns out the only thing we ever needed that Context for was its "getDatabasePath()" method, as that varies by application; but if you're using the SD card, you don't need that at all. (The above implementation supports both SD-card and phone-memory databases. Just in case.)&lt;br /&gt;&lt;br /&gt;So what happens in the DB object's instance(s)? Easy enough -&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;    public DB(Context ctx) {&lt;br /&gt;        this.mCtx = ctx;     &lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public DB open() throws SQLException {&lt;br /&gt;        if (mDbHelper==null)&lt;br /&gt;            mDbHelper = new DatabaseHelper(mCtx);&lt;br /&gt;        if (mDB==null || !mDB.isOpen())&lt;br /&gt;            mDB = mDbHelper.getWritableDatabase();&lt;br /&gt;        return this;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void close() {&lt;br /&gt;        mDbHelper.close();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean isOpen() {&lt;br /&gt;        return mDB!=null &amp;&amp; mDB.isOpen();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean deleteAll() {&lt;br /&gt;        mDB.delete("Message", null, null);&lt;br /&gt;//      ...other tables go here&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Message[] fetchMessages(String where, String[] args) throws SQLException {&lt;br /&gt;        Cursor cursor =  mDB.query(true, "Message",&lt;br /&gt;          new String[] {"_id", "messageTitle", "content" },&lt;br /&gt;          where, args, null, null, "dateCreated desc", null);&lt;br /&gt;&lt;br /&gt;        Message[] messages = new Message[cursor.getCount()];&lt;br /&gt;        cursor.moveToFirst();&lt;br /&gt;        int i=0;&lt;br /&gt;        while (!cursor.isAfterLast()) {&lt;br /&gt;            messages[i++] = new Message(cursor.getString(1), cursor.getString(2));&lt;br /&gt;            cursor.moveToNext();&lt;br /&gt;        }&lt;br /&gt;        cursor.close();&lt;br /&gt;        return messages;&lt;br /&gt;    }&lt;br /&gt;     &lt;br /&gt;    public void saveMessage(Message message) {&lt;br /&gt;        ContentValues values = new ContentValues();&lt;br /&gt;        values.put("messageTitle", message.getTitle());&lt;br /&gt;        values.put("content", message.getData());&lt;br /&gt;        values.put("messageType", message.getMessageType());&lt;br /&gt;        values.put("dateCreated", System.currentTimeMillis());&lt;br /&gt;        mDB.insert("Message", null, values);&lt;br /&gt;    }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;et voila: all your database access in one place, accessed by a simple DB.GetFor(Context context) call from anywhere in your app (and you don't even need the Context if you know the DB will live on the SD card!) and nicely abstracting out the crazy ten-positional-arguments-in-a-row "query" method on SQLiteDatabase.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-7333431738795067216?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/7333431738795067216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/04/sqlite-on-android.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7333431738795067216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7333431738795067216'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/04/sqlite-on-android.html' title='SQLite on Android'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-5650529419928130615</id><published>2010-03-01T19:02:00.000-08:00</published><updated>2010-03-01T19:15:42.000-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MotionEvent'/><category scheme='http://www.blogger.com/atom/ns#' term='touches'/><category scheme='http://www.blogger.com/atom/ns#' term='pan'/><category scheme='http://www.blogger.com/atom/ns#' term='touch'/><category scheme='http://www.blogger.com/atom/ns#' term='onTouchEvent'/><category scheme='http://www.blogger.com/atom/ns#' term='onDraw'/><category scheme='http://www.blogger.com/atom/ns#' term='zoom'/><category scheme='http://www.blogger.com/atom/ns#' term='dispatchDraw'/><category scheme='http://www.blogger.com/atom/ns#' term='onTouchListener'/><category scheme='http://www.blogger.com/atom/ns#' term='MapView'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>Responding to zooms and pans in Android's MapView</title><content type='html'>There's a curious lacuna in Android's MapView API: it's (relatively) easy to display a map, and overlay items atop a map; it's (relatively) easy to detect when an overlay item has been touched; but there's no straightforward way to work out when the user has touched anywhere else on the map. Because of the way MapView works, the usual onTouchListener() solution only works for one touch, and then fails.&lt;br /&gt;&lt;br /&gt;There is, however, a solution. Not a pretty one, but it works. The solution is to use your own subclass of MapView:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;public class ITRMapView extends MapView {&lt;br /&gt; public ITRMapView(android.content.Context context, android.util.AttributeSet attrs) {&lt;br /&gt;  super(context, attrs);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public ITRMapView(android.content.Context context, android.util.AttributeSet attrs, int defStyle) {&lt;br /&gt;  super(context, attrs, defStyle);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public ITRMapView(android.content.Context context, java.lang.String apiKey) {&lt;br /&gt;  super(context, apiKey);&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and within that subclass, override onTouchEvent. You're probably only interested in the UP action:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; public boolean onTouchEvent(MotionEvent ev) {&lt;br /&gt;  if (ev.getAction()==MotionEvent.ACTION_UP) {&lt;br /&gt;   //do your thing&lt;br /&gt;  }&lt;br /&gt;  return super.onTouchEvent(ev);&lt;br /&gt; }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;But what if you want to detect &lt;i&gt;zoom&lt;/i&gt; actions as well as touches? Simple, thought I: just override onDraw() and do the same thing. But you can't - MapView's onDraw() is &lt;b&gt;final&lt;/b&gt;!&lt;br /&gt;&lt;br /&gt;Catastrophe? Not quite. It turns out that overriding dispatchDraw() works just fine:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; int oldZoomLevel=-1;&lt;br /&gt;&lt;br /&gt; public void dispatchDraw(Canvas canvas) {&lt;br /&gt;  super.dispatchDraw(canvas);&lt;br /&gt;  if (getZoomLevel() != oldZoomLevel) {&lt;br /&gt;   //do your thing&lt;br /&gt;   oldZoomLevel = getZoomLevel();&lt;br /&gt;  }&lt;br /&gt; }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Et voila - you can programmatically detect and respond when the user pans and/or zooms your map. Really not so hard after all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-5650529419928130615?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/5650529419928130615/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/03/responding-to-zooms-and-pans-in.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5650529419928130615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5650529419928130615'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/03/responding-to-zooms-and-pans-in.html' title='Responding to zooms and pans in Android&apos;s MapView'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-5681059039903354315</id><published>2010-02-08T12:12:00.000-08:00</published><updated>2010-02-08T12:16:15.571-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='resources'/><category scheme='http://www.blogger.com/atom/ns#' term='notification'/><category scheme='http://www.blogger.com/atom/ns#' term='maps'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Settings'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='preferences'/><category scheme='http://www.blogger.com/atom/ns#' term='building'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='push'/><title type='text'>Android vs. iPhone: A Developer's Perspective, part II</title><content type='html'>&lt;center&gt;&lt;b&gt;&lt;i&gt;Android vs. iPhone: A Programmer's Perspective - II&lt;/i&gt;&lt;/b&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;(See also &lt;a href="http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers.html"&gt;Part I&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;OS features&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The big winner here is Android, due to its multitasking. You can call other apps, eg a browser or a file-system picker, get results from them, and use those results in your own app; all these things are completely impossible on the iPhone, where (aside from a few special cases like iTunes) only one app may run at a time. (And even if you want to sacrifice yourself to launch another app, you can only do so in very restricted circumstances - if they have registered a URL scheme.)&lt;br /&gt;&lt;br /&gt;The browser special-case is worth mentioning; you can include browser windows in your own app, and mine do so in a few different places - but this is often not as full-featured or convenient to the user as opening up the full Safari browser.&lt;br /&gt;&lt;br /&gt;You can also write Android services that run in the background, and you can launch them at boot time (if the user permits) by registering for the BOOT_COMPLETED intent. What it doesn't really, provide, though, is "push" notifications (other than by faking it with eg Comet HTTP Push) which the iPhone does offer. That's the iPhone's only advantage in this category, though.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Phone features&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;The iPhone is locked down. You don't get direct Bluetooth API access; you do in Android. You don't get direct SMS access; you do in Android. For security reasons, they claim in Cupertino. At least (like Android) it now lets you access the proximity sensor.&lt;br /&gt;&lt;br /&gt;But to its credit, the features it does offer are a joy to work with. In particular, the built-in camera/preview screen (for iPhone) / picture selector (for iPod Touch) is excellent, and requires all of a half-dozen lines of code to launch and respond to. The Android camera code, last I looked at it, was much more complex.&lt;br /&gt;&lt;br /&gt;Location management is a little messy and complex on both systems, but overall Android's registration model is easier to work with than the iPhone's delegation model.&lt;br /&gt;&lt;br /&gt;The iPhone SDK comes with this daft notion that all settings for all apps should probably be in a single System Settings screen accessible from the main menu; you can roll your own, but it's inconvenient. Android, by contrast, lets you create a settings screen by simply writing XML, no Java required unless you want to customize it. On the other hand, the iPhone simply makes "your default settings" available, whereas Android provides the possibility of multiple sets, which is doubtless more flexible and powerful but also more annoying to work with.&lt;br /&gt;&lt;br /&gt;Accessing system and app resources (eg image files) is a little counterintuitive on Android; at compile time, it scans a predetermined bunch of directories, and automatically builds an "R" file with a bunch of final static ints, each of which uniquely identifies a resource; you then use those in code to access resources. (There's also an Android.R for built-in-resources.) This is confusing at first, but fine once you get used to it.&lt;br /&gt;&lt;br /&gt;The iPhone makes the basics easy for you in code - &lt;PRE&gt;[Image imageNamed:@"myImage.png"]&lt;/PRE&gt; - but if you want to go beyond that, the whole resource-bundling thing is less than intuitive, and while I had no trouble accessing bundle resources, I never felt like I had a clear idea of what was actually going on, unlike with Android.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Screen building&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;No sense pussyfooting around: when it comes to actually building the screens of your app, the iPhone has a massive advantage. It provides an excellent WYSIWYG tool, and the components it offers - buttons, lists, etc - mostly just look a whole lot nicer and sexier than the Android ones.&lt;br /&gt;&lt;br /&gt;(That said, I have two minor complaints about the iPhone UI components: 1) No drop-down options in menus - instead you either have to code an ActionView or use one of the huge screen-eating spinners. 2) The absence of a border around TextViews does not look good and just serves to confuse users.)&lt;br /&gt;&lt;br /&gt;In general, though, the iPhone wins here. The delegate model of TableViewController gives you powerful and fine-grained control far more easily than the adapter model of ListActivity. As far as "complicated, hard-to-work-with, but useful subclasses of your basic list view" goes, I'll take the iPhone's LocalizedIndexedCollation over Android's ExpandableListView, though I sure wish both were more developer-friendly.&lt;br /&gt;&lt;br /&gt;And then there's maps. Jeez. In iPhone, if you want to add a custom marker for a map, then in the ViewController for that screen, you just override a method and write six lines of code:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;- (MKAnnotationView *)mapView:(MKMapView *)myMapView viewForAnnotation:(id &lt;MKAnnotation&gt;)annotation {&lt;br /&gt;  NSString viewImageName=@"myImage.jpg";&lt;br /&gt;  MKAnnotationView *myView = (MKAnnotationView*)[myMapView dequeueReusableAnnotationViewWithIdentifier:viewImageName];&lt;br /&gt;  if (myView==nil) {&lt;br /&gt;   myView = [[[MKAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:viewImageName] autorelease];&lt;br /&gt;   myView.image = [UIImage imageNamed:viewImageName];&lt;br /&gt;  }&lt;br /&gt;  return myView;&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;In Android, first you have to write a whole new inner class that extends ItemizedOverlay&lt;OverlayItem&gt;, eg:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; class ListingsOverlay extends ItemizedOverlay&lt;OverlayItem&gt; {&lt;br /&gt;  private ArrayList&lt;OverlayItem&gt; overlays=new ArrayList&lt;OverlayItem&gt;();&lt;br /&gt;&lt;br /&gt;  public ListingsOverlay(android.graphics.drawable.Drawable defaultMarker) {&lt;br /&gt;   super(defaultMarker);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void addOverlay(OverlayItem overlay, Drawable marker) {&lt;br /&gt;   super.boundCenterBottom(marker);&lt;br /&gt;   overlay.setMarker(marker);&lt;br /&gt;   overlays.add(overlay);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  // necessary because populate() is protected&lt;br /&gt;  public void doPopulate() {&lt;br /&gt;   populate();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  protected OverlayItem createItem(int i) {&lt;br /&gt;   return(overlays.get(i));&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  @Override&lt;br /&gt;  public int size() {&lt;br /&gt;   return(overlays.size());&lt;br /&gt;  }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and then something like this:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;      mapView = (MapView) findViewById(R.id.mapview);&lt;br /&gt;      mapOverlays = mapView.getOverlays();&lt;br /&gt;      mapOverlays.removeAll(mapOverlays);&lt;br /&gt;&lt;br /&gt;      Drawable drawable = getResources().getDrawable(R.drawable.map_fave);&lt;br /&gt;      itemizedOverlay = new ListingsOverlay(drawable);&lt;br /&gt;      OverlayItem overlay = new OverlayItem(point, mappable.getIDString(), mappable.getMapDetailText());&lt;br /&gt;      Drawable marker = MappableItem.GetMarkerForMappable(ITRMapView.this, mappable.getCategory());&lt;br /&gt;      itemizedOverlay.addOverlay(overlay, marker);&lt;br /&gt;      itemizedOverlay.doPopulate();&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Menus and navigation are also easier and prettier on the iPhone; you can add sleek-looking buttons and toolbars and simply set them to call the selector of your choice, and you get a great NavigationController for iTunes-like interfaces, plus sexy animation. No real equivalent on Android.&lt;br /&gt;&lt;br /&gt;On the other hand, Android's XML layouts, while tedious and irritating, and not as pretty or near as exact as the iPhone's WYSIWYG, do work well once you get the hang of them - and they make it much easier to support multiple screen sizes and different orientations. (My iPhone app simply doesn't do landscape orientation; my Android app handles it almost perfectly, without me ever having thought about it.) This wasn't a big deal for the iPhone until last month - but apps that previously were confident of a 320x480 screen now have to deal with the iPad.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;The Internet&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Accessing web services and launching in-app web views is easy and effective in both Android and iPhone. Edge to the latter, though; there are a couple of weird little bugs with Android's WebViews (though they can be worked around with ease) and the iPhone gives you both more SDK options and better documentation.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Release&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Building an iPhone app is kind of scary. You're suddenly reminded that under the hood it's terrifying C++; the "Build" screens are full of dozens if not hundreds of byzantine, cryptic, intimidating options for compiling, precompiling, linking, etc., and you find yourself desperately hoping you've set up your import libraries perfectly and suddenly very careful not to touch anything.&lt;br /&gt;&lt;br /&gt;That said, XCode works really well. (Have I mentioned that debugging is far easier with the iPhone SDK? Debugging is far easier with the iPhone SDK. With Android I usually wind up resorting to debugging with log messages.) What does not work really well is Apple's paranoid certification hegemony. God forbid that anyone run an app without going through the App Store!&lt;br /&gt;&lt;br /&gt;So you need to go to Apple's site and futz around with it and with device IDs and create and download separate certificates for debug and release, and your temporary "provisioning" device certificates expire every three months, and while it is theoretically possible to build an app for someone else's device, email it to them, and have them install it, I have yet to actually succeed at this, despite repeated attempts. (It's somewhat easier if their device is plugged in to your machine.)&lt;br /&gt;&lt;br /&gt;You know how it works on Android?&lt;br /&gt;- You build your app.&lt;br /&gt;- You sign your app. (Which Eclipse can take care of with a simple wizard.)&lt;br /&gt;- Anyone in the whole world who wants to can now download and run the app.&lt;br /&gt;&lt;br /&gt;There's a slight pitfall if you're working with Google Maps - you have to jump through hoops like Apple's to create separate debug and release Maps API Keys, and ensure you're using the right one - but by and large, it's miles easier and better than trying to finagle your way into Apple's walled garden.&lt;br /&gt;&lt;br /&gt;Plus, if you've built an app and released it, and found some sort of subtle bug? With Android, you can fix that and have a new version up on the Android Market in five minutes. With Apple, it's ... a week? A month? Who knows? App Store approval is an infuriating black box.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Overall&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;They're both excellent systems. They both have their pros and cons. Overall I would rate the iPhone as better, both in terms of what you can do with it and how - but Android is superior in fundamental ways (eg multitasking and memory management) and catching up fast in terms of results. If Apple doesn't watch out, and move fast, they're going to find themselves superseded soon. Maybe this year.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-5681059039903354315?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/5681059039903354315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers_08.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5681059039903354315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5681059039903354315'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers_08.html' title='Android vs. iPhone: A Developer&apos;s Perspective, part II'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-7323652074744402791</id><published>2010-02-07T20:30:00.001-08:00</published><updated>2010-02-08T19:49:47.110-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='compare'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='comparison'/><category scheme='http://www.blogger.com/atom/ns#' term='IDE'/><category scheme='http://www.blogger.com/atom/ns#' term='developer'/><category scheme='http://www.blogger.com/atom/ns#' term='persistence'/><category scheme='http://www.blogger.com/atom/ns#' term='multithreading'/><category scheme='http://www.blogger.com/atom/ns#' term='theads'/><category scheme='http://www.blogger.com/atom/ns#' term='contrast'/><category scheme='http://www.blogger.com/atom/ns#' term='environment'/><category scheme='http://www.blogger.com/atom/ns#' term='language'/><title type='text'>Android vs. iPhone: A Developer's Perspective, part I</title><content type='html'>&lt;center&gt;&lt;b&gt;&lt;i&gt;Android vs. iPhone: A Programmer's Perspective&lt;/i&gt;&lt;/b&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;(See also &lt;a href="http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers_08.html"&gt;Part II&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;I've spent the last month or so building both Android and iPhone versions of my app &lt;b&gt;iTravel&lt;/b&gt;. (See &lt;a href="http://wetravelright.com/"&gt;http://wetravelright.com/&lt;/a&gt; for details and links.) Which gives me a pretty good perspective from which to compare and contrast the Android and iPhone environments and SDKs. Hence I give you the following head-to-head analysis, from a developer's point of view:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Language&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Non-programmers often think that one's language of choice is a big deal, but really, once you've learned two or three programming languages, picking up another is generally something you can do in a day or two. That said, there are often substantive differences. And this is definitely true of Java (for Android) and Objective-C (for iPhone.)&lt;br /&gt;&lt;br /&gt;Let's start off with the really annoying stuff: Objective-C's memory management. By which I mean, its complete lack of any. Programmers have to manually allocate and release memory when writing for the iPhone SDK, which is positively medieval. If you fail to do so, and there are many pitfalls, then you leak memory which is lost until the device reboots. This is awful. (And it's no longer true of the Mac SDK, incidentally; but the iPhone is behind the times.)&lt;br /&gt;&lt;br /&gt;There are other annoyances. You have two files to contend with for every class - a .h and a .c file. Which is inconvenient and complicating and inelegant. And suppose you have a basic, bog-standard instance variable. You generally wind up declaring it in, count 'em, not one, not two, not three, but four different places.&lt;br /&gt;&lt;br /&gt;In your .h:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;{&lt;br /&gt; NSObject *object&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) NSObject *object&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;In your .c;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@synthesize object&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;and later, in dealloc(),&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;[object release];&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;Whereas with Java, you have one single .java file, which in general will have&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;private Object object;&lt;br /&gt;public Object getObject() { return object; }&lt;br /&gt;private void setObject(Object o) { object=o; }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;all in one place. I know which one I think is easier.&lt;br /&gt;&lt;br /&gt;But. On the other hand. Objective-C is sort of the bastard son of C, which is awful, and Smalltalk, which is awesome. As a result, it has Smalltalk-like features like selectors:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;[caller performSelector:@selector(myFunctionName)]&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;...in short, you can use functions as (more or less, ish) first-class objects. Try that in Java and if memory serves you'll most likely wind up in the irritating labyrinth of reflection. Plus, you get options like "doesNotRecognizeSelector:", which can be easily misused, but is potentially very powerful, and does not exist in Java.&lt;br /&gt;&lt;br /&gt;On the other hand, Objective C is really annoyingly logorrheic (meaning wordy) especially when it comes to string handlers. Suppose you have strings A and B, and you wish to combine them into string C. In Java, the syntax is&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;C=A+B;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;whereas in Objective-C, you type&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;C = [A stringByAppendingString: B]&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;Or suppose you want the location of the last slash in string A. Java:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;n=A.lastIndexOf("/");&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;Objective-C:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;n = [A range ofString:@"/" options:NSBackwardSearch].location;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;I much prefer Java's syntax and simplicity. But I do admire Objective-C's flexibility.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Development environment&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;By this I mean: the "integrated development environment" in which coders work; the documentation for the IDE, the language, and the libraries; the testing and source-code support; and all the stuff that is meant to help you write better code faster.&lt;br /&gt;&lt;br /&gt;The Android and iPhone IDEs and documentation really incarnate the attitudes of the two companies in question. Android doesn't exactly have an IDE of its own, although they recommend that you use the Android plug-in for the open-source Eclipse IDE, which is slow, irritating, and buggy in various minor ways. The documentation is written by smart people for smart people, with little handholding. Flashy graphics are minimal to nonexistent. And a lot of important stuff is still handled by tools meant to be run from a shell rather than a GUI. But the search function is excellent.&lt;br /&gt;&lt;br /&gt;Apple's IDE is slick, seamless and powerful. It comes with a visual tool to help you lay out the screens of your app. The documentation is full of step-by-step guides (although they are often oddly lacking or confusing) and high-quality graphics and other visualizations.&lt;br /&gt;&lt;br /&gt;I have many complaints about both. Eclipse is slow and annoying, and I could only get Android's JUnit test harness to run successfully from a terminal window, rather than the IDE; similarly, I had to use shell tools to sign packages, get a fingerprint for a Maps IDE, install a package on my phone, etc. All of which really calls into question the I in IDE.&lt;br /&gt;&lt;br /&gt;On the other hand, at last the external unit-testing actually works; XCode's built-in harness is so bad that Google built and released an entirely separate one, which has the advantage of actually functioning correctly. On the other hand, its Subversion integration is excellent, which is not true of Eclipse.&lt;br /&gt;&lt;br /&gt;Both have plenty of official and unofficial online support, as well, at official support sites and places like Stack Overflow. Android's open-source ethos gives it a big advantage in terms of external packages, though; for instance, if you want to build a barcode scanner into an Android app, there's a whole open-source library out there for you, ready to be plugged in. For the iPhone? You'll have to roll your own. Sorry.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Multithreading&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;In phone apps multithreading is key, because the phone needs to remain as responsive as possible, so you need to do your heavy lifting behind the scenes, outside the main UI thread.&lt;br /&gt;&lt;br /&gt;Both the iPhone SDK and Android support multithreading, but the latter's is much more convenient, especially if you want to call back to the main thread once your background thread has done its thing. On the iPhone, you have to do something like:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void) doWebView {&lt;br /&gt; NSThread* htmlThread = [[NSThread alloc] initWithTarget:self selector:@selector(loadHtml) object:nil];&lt;br /&gt; [htmlThread start];&lt;br /&gt; [htmlThread release];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void) loadHtml {&lt;br /&gt; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];&lt;br /&gt; NSString *html = doUnroll ? [self getUnrolledData] : root.data;&lt;br /&gt; html=[NSString stringWithFormat: @"%@%@&amp;lt;/body&amp;gt;&amp;lt;/html&amp;gt;", [Util getBaseHtml], html];&lt;br /&gt; [self performSelectorOnMainThread:@selector(setHtml:) withObject:html waitUntilDone:NO];&lt;br /&gt; [pool release];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void) setHtml:(NSString*)html {&lt;br /&gt; [webView loadHTMLString:html baseURL:nil];&lt;br /&gt; [webView sizeToFit];&lt;br /&gt; [self.tableView setTableHeaderView:webView];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;Whereas on Android, you can use Java's equally irritating Runnable() framework, but Android provides an extremely convenient (and quite flexible) short form. Just subclass AsyncTask in an inner class:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;        new LoadHtmlTask().execute();&lt;br /&gt;&lt;br /&gt;    class LoadHtmlTask extends AsyncTask&lt;String, Integer, String&gt; {&lt;br /&gt;     protected String doInBackground(String... strings) {&lt;br /&gt;  String headerData = Settings.GetHtmlPrefix() + (unrolled ? getUnrolledData(viewRoot) : viewRoot.getData());&lt;br /&gt;  return headerData;&lt;br /&gt;     }     &lt;br /&gt;&lt;br /&gt;     protected void onPostExecute(String results) {&lt;br /&gt;  mHeaderView.loadDataWithBaseURL("local", headerData, "text/html", "utf-8", "");&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;which seems much more encapsulated and intuitive to me.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Persistence&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;On paper, the iPhone environment has a big advantage here: it features the Core Data object-persistence layer above the SQLite database, whereas Android requires direct DB access.&lt;br /&gt;&lt;br /&gt;For me, though, direct DB access was not awful, and given the constraints of my app, arguably simpler than jumping through all of Core Data's hoops - using the separate tool to declare a data model, having to rebuild your custom code every time you add a column, etc. All that without even getting thread safety.&lt;br /&gt;&lt;br /&gt;However, I'm totally comfortable writing SQL, not all developers are, and my app had pretty straightforward DB requirements. Core Data is undeniably more elegant and ultimately better. Plus you get nifty little features like shake-to-undo, semi-automatic migration data models in installed apps, etc. And it's not like the Android tools are particularly easy to work with. Check out the method in android.database.sqlite.SQLiteDatabase you use to perform a query:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;public Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;As a (Google employee) friend of mine said, "Holy positional parameters, Batman!" Needless to say, one quickly wraps this monstrosity in other methods less prone to grievous error...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers_08.html"&gt;Part II&lt;/a&gt; of this post compares and contrasts system features, phone features, settings and resources, screen building, internet connectivity, and the app install/release process. (It does not compare and contrast graphics programming, as my apps are data-heavy not graphics-heavy.) Don't touch that dial.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-7323652074744402791?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/7323652074744402791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7323652074744402791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7323652074744402791'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/02/android-vs-iphone-developers.html' title='Android vs. iPhone: A Developer&apos;s Perspective, part I'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-1715043005552085342</id><published>2010-01-31T16:09:00.000-08:00</published><updated>2010-01-31T16:31:22.475-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SQLite'/><category scheme='http://www.blogger.com/atom/ns#' term='WebView'/><category scheme='http://www.blogger.com/atom/ns#' term='invalidate'/><category scheme='http://www.blogger.com/atom/ns#' term='View'/><category scheme='http://www.blogger.com/atom/ns#' term='multithreading'/><category scheme='http://www.blogger.com/atom/ns#' term='cursor'/><category scheme='http://www.blogger.com/atom/ns#' term='background'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Back in the Android saddle</title><content type='html'>So now that versions 2.0 of my iPhone app are &lt;a href="http://wetravelright.com/"&gt;up and running on the App Store&lt;/a&gt;, I've finally started writing an Android version. You may recall that this blog was supposed to be primarily about Android development from the get-go. We apologize for the eight-month sidetrack.&lt;br /&gt;&lt;br /&gt;Anyway, It's been going quite well. Both the Android and iPhone SDK have their pros and cons. I like XCode better than Eclipse, and I generally (though not always) prefer Core Data to direct DB queries; on the other hand, string processing is far easier in Java, Android's multithreading is easier to work with, I vastly prefer single .java files vs twinned .h/.c files, and I love not having to worry about manual memory management.&lt;br /&gt;&lt;br /&gt;The Android version is now mostly functionally complete. Things I have (sometimes re) learned which may be of interest:&lt;br /&gt;&lt;br /&gt;&lt;UL&gt;&lt;br /&gt;&lt;LI&gt;Both the Android and iPhone OSes restrict background threads from directly calling the user interface. With the iPhone, you need to use "performSelectorOnMainThread:"; with Android, you need to use "postInvalidate()" rather than "invalidate()" to get a View to redraw itself.&lt;br /&gt;&lt;LI&gt;I'm experimenting with a database-access model wherein all my Activities do the following:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; protected DB db;&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt;    protected void onCreate(Bundle savedInstanceState) {&lt;br /&gt;        super.onCreate(savedInstanceState);&lt;br /&gt;     db = new DB(this);&lt;br /&gt;     db.open();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt; @Override&lt;br /&gt;    protected void onResume() {&lt;br /&gt;  super.onResume();&lt;br /&gt;     db.open();&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt; @Override&lt;br /&gt;    protected void onPause() {&lt;br /&gt;  super.onPause();&lt;br /&gt;     db.close();     &lt;br /&gt;    }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;wherein DB encapsulates the database, and all database calls go through it.  This is a variant of the "DbHelper" model that you see in the Android tutorials, and imho and simpler and better one in most circumstances. It's mostly implemented as per the above in abstract superclasses of the instantiated activities.&lt;br /&gt;&lt;br /&gt;You then have accessor methods such as&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;  db.saveListing(listing);&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;which is handled pretty much as per the tutorials, and&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;  Listing[] listings = db.fetchListingsForPage(Util.SQLize(pageURL));&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;which calls&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;    public Listing[] fetchListingsForPage(String pageURL) throws SQLException {&lt;br /&gt;     return fetchListingsFor("pageURL LIKE '"+pageURL+"%'", (String[])null);&lt;br /&gt;    }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;which in turn calls the generic&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;    public Listing[] fetchListingsFor(String where, String... args) throws SQLException {&lt;br /&gt;        Cursor cursor =  mDB.query(true, "Listing",&lt;br /&gt;          new String[] {"_id", "name", "data", "isFlagged", "category", "address", "url",&lt;br /&gt;          "pageURL", "sectionName", "sectionNumber", "location", "downloadDate" },&lt;br /&gt;                where, args, null, null, null, null);&lt;br /&gt;&lt;br /&gt;     Listing[] listings = new Listing[cursor.getCount()];&lt;br /&gt;     cursor.moveToFirst();&lt;br /&gt;  int i=0;&lt;br /&gt;  while (!cursor.isAfterLast()) {&lt;br /&gt;   listings[i++] = new Listing(cursor.getLong(0), cursor.getString(1), cursor.getString(2), cursor.getInt(3),&lt;br /&gt;     cursor.getInt(4), cursor.getString(5), cursor.getString(6), cursor.getString(7), cursor.getString(8),&lt;br /&gt;     cursor.getString(8), cursor.getString(10), cursor.getLong(11));&lt;br /&gt;   cursor.moveToNext();&lt;br /&gt;  }&lt;br /&gt;  cursor.close();&lt;br /&gt;  return listings;&lt;br /&gt;    }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;which is in course incredibly ugly, but at least it only happens in one place.&lt;br /&gt;&lt;br /&gt;&lt;LI&gt;Here's an Activity that serves as a browser within your app, in its entirety.&lt;br /&gt;Layout:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;&lt;?xml version="1.0" encoding="utf-8"?&gt;&lt;br /&gt;&lt;WebView android:id="@+id/web" xmlns:android="http://schemas.android.com/apk/res/android"&lt;br /&gt; android:layout_width="fill_parent"&lt;br /&gt; android:layout_height="fill_parent"/&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;Code:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;public class WebDisplay extends ITRActivity {&lt;br /&gt; public static String KEY_URL = "url";&lt;br /&gt; public static String KEY_DATA = "data";&lt;br /&gt; &lt;br /&gt; String mUrl;&lt;br /&gt; String mData;&lt;br /&gt; &lt;br /&gt; @Override&lt;br /&gt; public void onCreate(Bundle savedInstanceState) {&lt;br /&gt;        super.onCreate(savedInstanceState);&lt;br /&gt;        setContentView(R.layout.web);&lt;br /&gt;  WebView web = (WebView) findViewById(R.id.web);&lt;br /&gt;&lt;br /&gt;     Bundle extras = getIntent().getExtras();&lt;br /&gt;  mUrl = extras == null ? null : extras.getString(KEY_URL);&lt;br /&gt;  mData = extras == null ? null : extras.getString(KEY_DATA);&lt;br /&gt;  &lt;br /&gt;  if (mUrl!=null)&lt;br /&gt;   web.loadUrl(mUrl);&lt;br /&gt;  else if (mData!=null)&lt;br /&gt;   web.loadData(mData.replace("%","&amp;#37;"), "text/html", "utf-8");  &lt;br /&gt;    } &lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;/UL&gt;&lt;br /&gt;&lt;br /&gt;More to come when I release v1.0, which should happen in the next ten days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-1715043005552085342?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/1715043005552085342/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/01/back-in-android-saddle.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/1715043005552085342'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/1715043005552085342'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/01/back-in-android-saddle.html' title='Back in the Android saddle'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-8679671288223285429</id><published>2010-01-18T18:24:00.001-08:00</published><updated>2010-01-31T16:29:48.214-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NSThread'/><category scheme='http://www.blogger.com/atom/ns#' term='CoreData'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='multithread'/><category scheme='http://www.blogger.com/atom/ns#' term='UIApplicationDelegate'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='NSManagedObjectContext'/><category scheme='http://www.blogger.com/atom/ns#' term='SDK'/><category scheme='http://www.blogger.com/atom/ns#' term='NSPersistentStoreCoordinator'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrent'/><category scheme='http://www.blogger.com/atom/ns#' term='threading'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Using Core Data in a multithreaded environment.</title><content type='html'>You have to do a lot of iPhone stuff in background threads, to maintain a snappily responsive user interface. But if you're using Core Data, well - to quote &lt;a href="http://developer.apple.com/Mac/library/documentation/Cocoa/Conceptual/CoreData/Articles/cdMultiThreading.html"&gt;the docs&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Managed objects are not thread safe [...] Core Data does not present a situation where reads are "safe" but changes are "dangerous"—every operation is "dangerous" because every operation can trigger faulting.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Sound intimidating? No worries: it's a piece of cake. Just wrap the code where you get your ManagedObjectContext in a method something like this:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@implementation Util&lt;br /&gt;&lt;br /&gt;+(NSManagedObjectContext*)getManagedObjectContext {&lt;br /&gt; UIApplication *app = [UIApplication sharedApplication];&lt;br /&gt; MyAppDelegate *appDelegate = app.delegate;&lt;br /&gt; if ([NSThread isMainThread])&lt;br /&gt;  return [appDelegate managedObjectContext];&lt;br /&gt; else&lt;br /&gt;  return [appDelegate getMOCFor:[NSThread currentThread]];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and add the following method to your application delegate:&lt;br /&gt;(where "threadMOCs", obviously, is a properly defined and synthesized ivar.)&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;- (NSManagedObjectContext*) getMOCFor:(NSThread*) thread {&lt;br /&gt; if (!threadMOCs)&lt;br /&gt;  self.threadMOCs=[NSMutableDictionary dictionaryWithCapacity:16];&lt;br /&gt; &lt;br /&gt; NSNumber *threadHash = [NSNumber numberWithInt:[thread hash]];&lt;br /&gt; if ([threadMOCs objectForKey:threadHash]==nil) {&lt;br /&gt;  NSManagedObjectContext *newMOC =  [[NSManagedObjectContext alloc] init];&lt;br /&gt;  NSError *error=nil;&lt;br /&gt;  NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"myDBName.sqlite"]];  &lt;br /&gt;  NSManagedObjectModel *model = [self managedObjectModel];&lt;br /&gt;  NSPersistentStoreCoordinator* threadPSC = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: model];&lt;br /&gt;  if (![threadPSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&amp;error])&lt;br /&gt;   { //handle error here }&lt;br /&gt;&lt;br /&gt;  [newMOC setPersistentStoreCoordinator: threadPSC];&lt;br /&gt;  [threadMOCs setObject:newMOC forKey:threadHash];&lt;br /&gt; }&lt;br /&gt; return [threadMOCs objectForKey:threadHash];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Et voila! A new object context and persistent store coordinare for every thread, which gives you full concurrent access to your data. (Though you probably still have to be careful about passing managed objects &lt;i&gt;across&lt;/i&gt; threads...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-8679671288223285429?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/8679671288223285429/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2010/01/using-core-data-in-multithreaded.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/8679671288223285429'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/8679671288223285429'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2010/01/using-core-data-in-multithreaded.html' title='Using Core Data in a multithreaded environment.'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-6675916599610841421</id><published>2009-11-30T14:18:00.001-08:00</published><updated>2009-12-16T08:17:38.731-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='chunks'/><category scheme='http://www.blogger.com/atom/ns#' term='limit'/><category scheme='http://www.blogger.com/atom/ns#' term='Images'/><category scheme='http://www.blogger.com/atom/ns#' term='BigTable'/><category scheme='http://www.blogger.com/atom/ns#' term='AppEngine'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='MemoryError'/><category scheme='http://www.blogger.com/atom/ns#' term='JPG'/><category scheme='http://www.blogger.com/atom/ns#' term='size'/><category scheme='http://www.blogger.com/atom/ns#' term='JPEG'/><category scheme='http://www.blogger.com/atom/ns#' term='chunking'/><title type='text'>How to store images larger than 1 megabyte in Google App Engine</title><content type='html'>Over the summer, Google App Engine raised its limits for web requests and responses from 1MB to 10MB, but kept the maximum size of any single database element at 1MB. If you try to exceed this, you'll get a MemoryError. You can find a fair amount of grief and woe and gnashing of teeth and wearing of sackcloth and ashes about this online.&lt;br /&gt;&lt;br /&gt;Which is kind of surprising, because it's not &lt;i&gt;that&lt;/i&gt; hard to break files up into chunks and store those chunks in the database separately. Here's what I did today for &lt;a href="news.bbc.co.uk/2/hi/8258501.stm"&gt;my current project&lt;/a&gt;, which stores data - including photos - uploaded from smartphones:&lt;br /&gt;&lt;br /&gt;First, we have to receive the uploaded image. Our uploads are two-phase - first data, then a photo - for various reasons. The data upload includes the image's file name; the photo upload is a basic form/multipart POST with exactly one argument (the filename) and its value (the file).&lt;br /&gt;&lt;br /&gt;So, in "main.py":&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;class SaveImage(webapp.RequestHandler):&lt;br /&gt;  def post(self):&lt;br /&gt;    entryHandler=ec.EntryHandler()&lt;br /&gt;    for arg in self.request.arguments():&lt;br /&gt;      file = self.request.get(arg)&lt;br /&gt;      response = entryHandler.saveImage(arg,file)&lt;br /&gt;      self.response.out.write(response)&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and in "ec.py":&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;class ImageChunk(db.Model):&lt;br /&gt;  entryRef = db.ReferenceProperty(Entry)&lt;br /&gt;  chunkIndex = db.IntegerProperty()&lt;br /&gt;  chunk = db.BlobProperty()&lt;br /&gt;&lt;br /&gt;class EntryHandler:&lt;br /&gt;  def saveImage(self, fileName, file):&lt;br /&gt;      results = Entry.all().filter("photoPath =", fileName).fetch(1)&lt;br /&gt;      if len(results)==0:&lt;br /&gt;        logging.warning("Error - could not find the entry associated with image name "+fileName)&lt;br /&gt;        return "Failed"&lt;br /&gt;      else:&lt;br /&gt;        MaxBTSize=1000000&lt;br /&gt;        entry = results[0]&lt;br /&gt;        marker=0&lt;br /&gt;        chunks=[]&lt;br /&gt;        while marker*MaxBTSize&amp;lt;len(file):&lt;br /&gt;          if MaxBTSize*(marker+1)&amp;gt;len(file):&lt;br /&gt;            chunk = ImageChunk(entryRef=entry, chunkIndex=marker, chunk=db.Blob(file[MaxBTSize*marker:]))&lt;br /&gt;          else:&lt;br /&gt;            chunk = ImageChunk(entryRef=entry, chunkIndex=marker, chunk=db.Blob(file[MaxBTSize*marker:MaxBTSize*(marker+1)]))&lt;br /&gt;          chunk.put()&lt;br /&gt;          marker+=1&lt;br /&gt;        logging.info("Successfully received image "+fileName)&lt;br /&gt;        return "Successfully received image "+fileName&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Pretty basic stuff: we chop the image up at each 1,000,000-byte mark, and put each chunk into its own ImageChunk DB object. &lt;br /&gt;&lt;br /&gt;Then, when we need to retrieve the image, in 'main.py':&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;class ShowImageWithKey(webapp.RequestHandler):&lt;br /&gt;  def get(self):&lt;br /&gt;    key = self.request.get('entryKey')&lt;br /&gt;    entryHandler = ec.EntryHandler()&lt;br /&gt;    image = entryHandler.getImageByEntryKey(key)&lt;br /&gt;    if image is not None:&lt;br /&gt;      self.response.headers['Content-Type'] = 'image/jpeg'&lt;br /&gt;      self.response.out.write(image)&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and in 'ec.py':&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;  def getImageByEntryKey(self, key):&lt;br /&gt;    chunks = db.GqlQuery("SELECT * FROM ImageChunk WHERE entryRef = :1 ORDER BY chunkIndex", key).fetch(100)&lt;br /&gt;    if len(chunks)==0:&lt;br /&gt;      return None&lt;br /&gt;&lt;br /&gt;    image=""&lt;br /&gt;    for chunkRow in chunks:&lt;br /&gt;      image+=chunkRow.chunk&lt;br /&gt;    return image&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Since db.Blob is a subtype of str, that's all you have to do. I don't understand why some people are so upset about this: it's mildly annoying that I had to write the above, but hardly crippling. At least with JPEGs, which is what we use. (But I don't see why any other file type would be more difficult; they're ultimately all just a bunch of bytes). Could hardly be easier ... well, until App Engine rolls out their &lt;a href="http://code.google.com/appengine/docs/roadmap.html"&gt;large file service&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;(&lt;i&gt;eta, Dec 14:&lt;/i&gt; which came out &lt;a href="http://googleappengine.blogspot.com/2009/12/app-engine-sdk-130-released-including.html"&gt;today!&lt;/a&gt; Meaning you can now disregard all the above and just use the new Blobstore instead.)&lt;br /&gt;&lt;br /&gt;(&lt;i&gt;eta, Dec 16:&lt;/i&gt; mmm, maybe not. Looked at the Blobstore in detail today, and it's really best suited for browser projects, not app or web-service stuff. The API for the blobs is very limited, and you can only access them via one-time-only URLs that App Engine puts in your HTML. You could scrape that, granted, but that's a pain in the ass, no less inelegant than the image-chunking solution above. It's experimental and subject to change, too. I think I'll hold out until its API improves.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-6675916599610841421?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/6675916599610841421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/11/how-to-store-images-larger-than-1.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/6675916599610841421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/6675916599610841421'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/11/how-to-store-images-larger-than-1.html' title='How to store images larger than 1 megabyte in Google App Engine'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-7296269377470149321</id><published>2009-11-04T08:34:00.000-08:00</published><updated>2010-01-31T16:30:10.768-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppStore'/><category scheme='http://www.blogger.com/atom/ns#' term='BigTable'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='AppEngine'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='iTravel'/><category scheme='http://www.blogger.com/atom/ns#' term='iTravelFree'/><category scheme='http://www.blogger.com/atom/ns#' term='Apple'/><category scheme='http://www.blogger.com/atom/ns#' term='Wikitravel'/><title type='text'>to infinity, and beyond!</title><content type='html'>I am pleased to report that my pet-project iPhone app, &lt;i&gt;iTravelFree&lt;/i&gt;, has passed the stern inspection of Apple's App Store and is now available for download worldwide. For app links, a screenshot-laden tutorial, and help and FAQ files, see here: &lt;a href="http://wetravelright.com/"&gt;www.wetravelright.com&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;(Yeah, crappy URL, I know, but all the good ones were taken.)&lt;br /&gt;&lt;br /&gt;Since this is my tech blog let me wax about its architecture a bit. The iPhone app is pretty straightforward: basically, it's a bunch of TableViewControllers, many of which include WebViews, along with a MapViewController, all pointing to a bunch of CoreData records. Nothing extraordinarily fancy by any means.&lt;br /&gt;&lt;br /&gt;The server side is more interesting: it's a Google App Engine service, written in Python, that fetches, caches, and parses Wikitravel pages for the app. This gives me a single point of access to the data flow, lets me do things like convert addresses to lat/long location, cuts down on bandwidth for both Wikitravel (thanks to the caching) and the phone app (thanks to the parsing and stripping out of extraneous info.)&lt;br /&gt;&lt;br /&gt;The general architecture - phone app plus App Engine service - is actually really powerful and easy to work with. Basically, it's a distributed version of the classic Model-View-Controller architecture, where the phone is the view, the App Engine service is the controller, and whatever data you're accessing is the model.  This lets you do all the heavy-lifting computation on the server side, which is where it belongs, and keep the phone (and its puny processor) focused almost purely on the UI.&lt;br /&gt;&lt;br /&gt;I do have some reservations about the BigTable data store that App Engine uses, but they don't apply to projects like this, with relatively simple storage requirements and no data mining. &lt;br /&gt;&lt;br /&gt;I wrote it in, hrmm, about six weeks all told, starting in July. (Obviously it's been much more than six weeks since then, but I had full-time work starting August so could only work on this in fits and spurts on the side.)&lt;br /&gt;&lt;br /&gt;Anyway - the app is in pretty good shape, but there's more work to be done on the server side, so it's still basically in beta test. Take a look, download it, play around, and let me know what you think -&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-7296269377470149321?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/7296269377470149321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/11/to-infinity-and-beyond.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7296269377470149321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7296269377470149321'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/11/to-infinity-and-beyond.html' title='to infinity, and beyond!'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-8143354814246729599</id><published>2009-10-14T14:11:00.001-07:00</published><updated>2009-10-14T14:28:04.274-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='drag-and-drop'/><category scheme='http://www.blogger.com/atom/ns#' term='drag'/><category scheme='http://www.blogger.com/atom/ns#' term='JQuery'/><category scheme='http://www.blogger.com/atom/ns#' term='sortable'/><category scheme='http://www.blogger.com/atom/ns#' term='bind'/><category scheme='http://www.blogger.com/atom/ns#' term='drop'/><category scheme='http://www.blogger.com/atom/ns#' term='clone'/><category scheme='http://www.blogger.com/atom/ns#' term='draggable'/><category scheme='http://www.blogger.com/atom/ns#' term='droppable'/><title type='text'>jQuery, how do I love thee?</title><content type='html'>So I spent a few days doing some JavaScript, which is not my favourite thing. Or at least it didn't use to be. But now that I have discovered &lt;a href="http://jquery.com/"&gt;JQuery&lt;/a&gt;, I gotta say, it's a whole lot more tolerable than it used to be.&lt;br /&gt;&lt;br /&gt;JQuery makes some things that I would previous have considered all but impossible downright easy. Take drag-and-drop inside the browser, f'rinstance. To do it yourself, you'd have to deal with, jeez, I don't even &lt;i&gt;know&lt;/i&gt; what, DOM craziness, layers, who knows. In jQuery? Well, lemme give you a quick example, stripped of all the actual business logic, of a drag-and-drop form builder:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;&amp;lt;html&amp;gt;&amp;lt;head&amp;gt;&lt;br /&gt;     &amp;lt;script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt; &amp;lt;script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;style type="text/css"&amp;gt;&lt;br /&gt;        .columns:after {&lt;br /&gt;            content: ".";&lt;br /&gt;            display: block;&lt;br /&gt;            height: 0;&lt;br /&gt;            clear: both;&lt;br /&gt;            visibility: hidden;&lt;br /&gt;        }&lt;br /&gt;        * html .columns {height: 1%;}&lt;br /&gt;        .columns{ display:inline-block; }&lt;br /&gt;        .columns{ display:block; }&lt;br /&gt;        .columns .column{&lt;br /&gt;          float:left;&lt;br /&gt;          display:inline;&lt;br /&gt;          min-height:360px;&lt;br /&gt;        }&lt;br /&gt;        .columns .last{ float:right; }&lt;br /&gt;        .columns .first{ width:180px; background-color:#ffeeee; }&lt;br /&gt;        .columns .second{ width:280px; margin-left:10px; background-color:#eeffee; }&lt;br /&gt;        .columns .last{ width:210px; background-color:#eeeeff}&lt;br /&gt;&lt;br /&gt;        .footers{ display:inline-block; }&lt;br /&gt;        .footers{ display:block; }&lt;br /&gt;        .footers .footer{&lt;br /&gt;          float:left;&lt;br /&gt;          display:inline;&lt;br /&gt;        }&lt;br /&gt;        .footers .last{ float:right; }&lt;br /&gt;        .footers .first{ width:180px; background-color:#eeeeee; }&lt;br /&gt;        .footers .second{ width:280px; margin-left:10px; background-color:#eeeeee; }&lt;br /&gt;        .footers .last{ width:210px; background-color:#eeeeee}&lt;br /&gt;&lt;br /&gt;        .formLineEdit { text-align: right; }&lt;br /&gt;        .optionLabel { float:right; text-align: right; }&lt;br /&gt;        .label { font-weight: bold; vertical-align: text-top; }&lt;br /&gt;        .formInput { text-align: right; vertical-align: text-top; }&lt;br /&gt;        .editHeader { text-align: right; }&lt;br /&gt;        .selectOption { text-align: right; }&lt;br /&gt;        #error { text-align: center; color: #ff0000; }&lt;br /&gt;        #success { text-align: center; color: #00ff00; }&lt;br /&gt;        #listForms { float: left; text-align: left; }&lt;br /&gt;        #viewHelp { float: right; text-align: right; }&lt;br /&gt;        #inputOptions { text-align: right; }&lt;br /&gt;        #detailHeader { text-align: right; }&lt;br /&gt;        #formName {background-color: #eeeeee; }&lt;br /&gt;        #formVersionNumber {background-color: #eeeeee; }&lt;br /&gt;        #formSubmitButton {text-align:right; background-color: #eeeeee; }&lt;br /&gt;    &amp;lt;/style&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;script&amp;gt;&lt;br /&gt;        var totalItems=0;&lt;br /&gt;        var currentlyEditing=null;&lt;br /&gt;        var lastEdited=null;&lt;br /&gt;&lt;br /&gt;        $(function() {&lt;br /&gt;            //make divs created out of XML clickable&lt;br /&gt;            var destClicked=false;&lt;br /&gt;            $("#destination").mousedown( function() {&lt;br /&gt;                if (!destClicked) { //only do this once, or it might get messy&lt;br /&gt;                    $("#destination").children().each( function() {&lt;br /&gt;                        $(this).bind("click", function() {&lt;br /&gt;                            showEditDetailsFor($(this));&lt;br /&gt;                        });&lt;br /&gt;                    });&lt;br /&gt;                }&lt;br /&gt;                destClicked=true;&lt;br /&gt;            });&lt;br /&gt;            &lt;br /&gt;            //set up drag-and-drop stuff&lt;br /&gt;            $("#textInput").draggable(&lt;br /&gt;                {   connectToSortable:'#destination',&lt;br /&gt;                    cursor:'move',&lt;br /&gt;                    helper:'clone',&lt;br /&gt;                }&lt;br /&gt;            );&lt;br /&gt;            $("#longText").draggable(&lt;br /&gt;                {   connectToSortable:'#destination',&lt;br /&gt;                    cursor:'move',&lt;br /&gt;                    helper:'clone',&lt;br /&gt;                }&lt;br /&gt;            );&lt;br /&gt;            $("#selectMultiple").draggable(&lt;br /&gt;                {   connectToSortable:'#destination',&lt;br /&gt;                    cursor:'move',&lt;br /&gt;                    helper:'clone',&lt;br /&gt;                }&lt;br /&gt;            );&lt;br /&gt;            $("#selectOne").draggable(&lt;br /&gt;                {   connectToSortable:'#destination',&lt;br /&gt;                    cursor:'move',&lt;br /&gt;                    helper:'clone',&lt;br /&gt;                }&lt;br /&gt;            );&lt;br /&gt;            $("#destination").sortable(&lt;br /&gt;                {&lt;br /&gt;                    change: function(event, ui) {&lt;br /&gt;                        ui.placeholder.css({visibility: 'visible', border : '2px solid yellow'});&lt;br /&gt;                    },&lt;br /&gt;                    start: function(event, ui) {&lt;br /&gt;                        ui.placeholder.css({visibility: 'visible', border : '2px solid yellow'});&lt;br /&gt;                        var tempID=ui.item.attr("id");&lt;br /&gt;                        if (tempID.indexOf("_")==-1) {&lt;br /&gt;                            tempID=tempID+"_"+totalItems++;&lt;br /&gt;                            ui.item.attr({id:tempID});&lt;br /&gt;                        }&lt;br /&gt;                    },&lt;br /&gt;                    stop: function(event, ui) {&lt;br /&gt;                        //load element details, and ensure they'll show up again when this item is clicked&lt;br /&gt;                        showEditDetailsFor(ui.item);&lt;br /&gt;                        ui.item.bind("click", function() {&lt;br /&gt;                            showEditDetailsFor(ui.item);&lt;br /&gt;                        });&lt;br /&gt;                    },&lt;br /&gt;                }&lt;br /&gt;            );&lt;br /&gt;        });&lt;br /&gt;&lt;br /&gt;        function showEditDetailsFor ( object ) {&lt;br /&gt;//            object.effect("highlight", {}, 3000);&lt;br /&gt;            currentlyEditing=object.attr("id");&lt;br /&gt;            if (currentlyEditing==lastEdited)&lt;br /&gt;                return;&lt;br /&gt;&lt;br /&gt;            $(".formLineEdit").hide();&lt;br /&gt;            $("#inputOptions").show();&lt;br /&gt;&lt;br /&gt;            if (object.attr("id").indexOf("textInput")==0) {&lt;br /&gt;                var inputLabel = $.trim(object.find(".label").text());&lt;br /&gt;                var inputID = object.find("input[name=inputID]").val();&lt;br /&gt;                if (inputLabel!="Text Input" || object.find("input[name=inputID]").attr("name")!="inputID") {&lt;br /&gt;                    $("#textInputEdit").find("input[name=textInputLabel]").val(inputLabel);&lt;br /&gt;                    $("#textInputEdit").find("input[name=textInputValue]").val(inputID);&lt;br /&gt;                }&lt;br /&gt;                $("#textInputEdit").show();&lt;br /&gt;            }&lt;br /&gt;            else if (object.attr("id").indexOf("longText")==0) {&lt;br /&gt;                var inputLabel = $.trim(object.find(".label").text());&lt;br /&gt;                var inputID = object.find("textarea:first").val();&lt;br /&gt;                if (inputLabel!="Long Text" || inputID!="") {&lt;br /&gt;                    $("#longTextEdit").find("input[name=longTextLabel]").val(inputLabel);&lt;br /&gt;                    $("#longTextEdit").find("input[name=longTextValue]").val(inputID);&lt;br /&gt;                }&lt;br /&gt;                $("#longTextEdit").show();&lt;br /&gt;            }&lt;br /&gt;            else if (object.attr("id").indexOf("selectMultiple")==0) {&lt;br /&gt;                var inputLabel = $.trim(object.find(".label:first").text());&lt;br /&gt;                var inputID = object.find("input:last").attr("name");&lt;br /&gt;                if (inputLabel!="Select Multiple" || inputID!="selectMulti") {&lt;br /&gt;                    $("#selectMultiEdit").find("input[name=selectMultiLabel]").val(inputLabel);&lt;br /&gt;                    $("#selectMultiEdit").find("input[name=selectMultiValue]").val(inputID);&lt;br /&gt;                    blankOption=$("#selectMultiEdit &amp;gt; .selectOption").remove();&lt;br /&gt;                    var addedChild=false;&lt;br /&gt;                    object.children(".formInput").each( function() {&lt;br /&gt;                        optionClone=blankOption.clone();&lt;br /&gt;                        var optionLabel=$(this).find(".optionLabel").text();&lt;br /&gt;                        optionClone.find("input[name=multiOptionLabel]").val(optionLabel);&lt;br /&gt;                        var optionValue=$(this).find("input:first").attr("value");&lt;br /&gt;                        optionClone.find("input[name=multiOptionValue]").val(optionValue);&lt;br /&gt;                        cloneRemoveButton = optionClone.find("input[name=removeSelectMultiOption]");&lt;br /&gt;                        cloneRemoveButton.bind("mouseup", function() {&lt;br /&gt;                            $(this).parent().remove();&lt;br /&gt;                        });&lt;br /&gt;                        $("#selectMultiEdit").append(optionClone);&lt;br /&gt;                        addedChild=true;&lt;br /&gt;                    });&lt;br /&gt;                    if (!addedChild)&lt;br /&gt;                        $("#selectMultiEdit").append(blankOption);&lt;br /&gt;                } //don't show defaults&lt;br /&gt;                $("#selectMultiEdit").show();&lt;br /&gt;            }&lt;br /&gt;            else if (object.attr("id").indexOf("selectOne")==0) {&lt;br /&gt;                var inputLabel = $.trim(object.find(".label:first").text());&lt;br /&gt;                var inputID = object.find("input:last").attr("name");&lt;br /&gt;                if (inputLabel!="Select One" || inputID!="selectOne") {&lt;br /&gt;                    $("#selectOneEdit").find("input[name=selectOneLabel]").val(inputLabel);&lt;br /&gt;                    $("#selectOneEdit").find("input[name=selectOneValue]").val(inputID);&lt;br /&gt;                    blankOption=$("#selectOneEdit &amp;gt; .selectOption").remove();&lt;br /&gt;                    var addedChild=false;&lt;br /&gt;                    object.children(".formInput").each( function() {&lt;br /&gt;                        optionClone=blankOption.clone();&lt;br /&gt;                        var optionLabel=$(this).find(".optionLabel").text();&lt;br /&gt;                        optionClone.find("input[name=oneOptionLabel]").val(optionLabel);&lt;br /&gt;                        var optionValue=$(this).find("input:first").attr("value");&lt;br /&gt;                        optionClone.find("input[name=oneOptionValue]").val(optionValue);&lt;br /&gt;                        cloneRemoveButton = optionClone.find("input[name=removeSelectOneOption]");&lt;br /&gt;                        cloneRemoveButton.bind("mouseup", function() {&lt;br /&gt;                            $(this).parent().remove();&lt;br /&gt;                        });&lt;br /&gt;                        $("#selectOneEdit").append(optionClone);&lt;br /&gt;                        addedChild=true;&lt;br /&gt;                    });&lt;br /&gt;                    if (!addedChild)&lt;br /&gt;                        $("#selectOneEdit").append(blankOption);&lt;br /&gt;                } //don't show defaults&lt;br /&gt;                $("#selectOneEdit").show();&lt;br /&gt;            }&lt;br /&gt;            lastEdited=currentlyEditing;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&amp;lt;center&amp;gt;&amp;lt;H3&amp;gt;Form Builder&amp;lt;/H3&amp;gt;&amp;lt;/center&amp;gt;&lt;br /&gt;&amp;lt;div id="success"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;div id="error"&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div id="header"&amp;gt;&lt;br /&gt;    &amp;nbsp;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;P/&amp;gt;&lt;br /&gt;&amp;lt;div class="columns"&amp;gt;&lt;br /&gt;    &amp;lt;div id="source" class="column first"&amp;gt;&lt;br /&gt;        &amp;lt;b&amp;gt;Form Elements&amp;lt;/b&amp;gt;&amp;lt;HR/&amp;gt;&lt;br /&gt;        &amp;lt;div id="textInput" class="formLine"&amp;gt;&lt;br /&gt;            &amp;lt;div class="label"&amp;gt;Text Input&amp;lt;/div&amp;gt;&amp;lt;div class="formInput"&amp;gt;&amp;lt;input name="inputID" /&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;HR/&amp;gt;&lt;br /&gt;        &amp;lt;div id="longText" class="formLine"&amp;gt;&lt;br /&gt;            &amp;lt;div class="label"&amp;gt;Long Text&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;div class="formInput"&amp;gt;&amp;lt;textarea name="textarea"&amp;gt;&amp;lt;/textarea&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;HR/&amp;gt;&lt;br /&gt;        &amp;lt;div id="selectMultiple" class="formLine"&amp;gt;&lt;br /&gt;            &amp;lt;div class="label"&amp;gt;Select Multiple&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;div class="formInput"&amp;gt;&amp;nbsp;&lt;br /&gt;                &amp;lt;input type="checkbox" name="selectMulti" value="one" /&amp;gt;&lt;br /&gt;                &amp;lt;div class="optionLabel"&amp;gt;One&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;div class="formInput"&amp;gt;&amp;nbsp;&lt;br /&gt;                &amp;lt;BR/&amp;gt;&amp;lt;input type="checkbox" name="selectMulti" value="two" /&amp;gt;&lt;br /&gt;                &amp;lt;div class="optionLabel"&amp;gt;Two&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;HR/&amp;gt;&lt;br /&gt;        &amp;lt;div id="selectOne" class="formLine"&amp;gt;&lt;br /&gt;            &amp;lt;div class="label"&amp;gt;Select One&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;div class="formInput"&amp;gt;&amp;nbsp;&lt;br /&gt;                &amp;lt;input type="radio" name="selectOne" value="one" /&amp;gt;&lt;br /&gt;                &amp;lt;div class="optionLabel"&amp;gt;One&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;div class="formInput"&amp;gt;&amp;nbsp;&lt;br /&gt;                &amp;lt;BR/&amp;gt;&amp;lt;input type="radio" name="selectOne" value="two" /&amp;gt;&lt;br /&gt;                &amp;lt;div class="optionLabel"&amp;gt;Two&amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt; &amp;lt;!--source--&amp;gt;&lt;br /&gt;    &amp;lt;div id="middle" class="column second"&amp;gt;&lt;br /&gt;        &amp;lt;b&amp;gt;Form&amp;lt;/b&amp;gt; (drag elements here)&lt;br /&gt;        &amp;lt;HR/&amp;gt;&lt;br /&gt;        &amp;lt;div id="destination"&amp;gt;&amp;nbsp;&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;HR/&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="details" class="column last"&amp;gt;&lt;br /&gt;        &amp;lt;div id="detailHeader"&amp;gt;&amp;lt;b&amp;gt;Element Details&amp;lt;/b&amp;gt;&amp;lt;HR/&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div id="textInputEdit" class="formLineEdit"&amp;gt;&lt;br /&gt;            &amp;lt;div class="editHeader"&amp;gt;&lt;br /&gt;                &amp;lt;b&amp;gt;Text Input&amp;lt;/b&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="textInputSubmit" value="Done" /&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="delete" value="Delete" /&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#labels" target="_blank"&amp;gt;Label&amp;lt;/a&amp;gt;&amp;lt;input name="textInputLabel" type="text" /&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#ids" target="_blank"&amp;gt;ID&amp;lt;/a&amp;gt;&amp;lt;input name="textInputValue" /&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div id="longTextEdit" class="formLineEdit"&amp;gt;&lt;br /&gt;            &amp;lt;div class="editHeader"&amp;gt;&lt;br /&gt;                &amp;lt;b&amp;gt;Long Text&amp;lt;/b&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="longTextSubmit" value="Done" /&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="delete" value="Delete" /&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#labels" target="_blank"&amp;gt;Label&amp;lt;/a&amp;gt;&amp;lt;input name="longTextLabel" /&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#ids" target="_blank"&amp;gt;ID&amp;lt;/a&amp;gt;&amp;lt;input name="longTextValue" /&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div id="selectMultiEdit" class="formLineEdit"&amp;gt;&lt;br /&gt;            &amp;lt;div class="editHeader"&amp;gt;&lt;br /&gt;                &amp;lt;b&amp;gt;Select Multiple&amp;lt;/b&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="selectMultiSubmit" value="Done" /&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="delete" value="Delete" /&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#labels" target="_blank"&amp;gt;Label&amp;lt;/a&amp;gt;&amp;lt;input name="selectMultiLabel" /&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#ids" target="_blank"&amp;gt;ID&amp;lt;/a&amp;gt;&amp;lt;input name="selectMultiValue" /&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;input type="submit" name="addSelectMultiOption" value="Add Option" /&amp;gt;&lt;br /&gt;            &amp;lt;HR/&amp;gt;&lt;br /&gt;            &amp;lt;div id="selectMultiOption" class="selectOption"&amp;gt;&lt;br /&gt;                &amp;lt;i&amp;gt;Option&amp;lt;/i&amp;gt;: &amp;lt;a href="/formHelp#editSelects" target="_blank"&amp;gt;Name&amp;lt;/a&amp;gt; &amp;lt;input name="multiOptionLabel" size=12/&amp;gt;&lt;br /&gt;                &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#editSelects" target="_blank"&amp;gt;Value&amp;lt;/a&amp;gt;&amp;lt;input name="multiOptionValue" size=12/&amp;gt;&lt;br /&gt;                &amp;lt;BR/&amp;gt;&amp;lt;input type="submit" name="removeSelectMultiOption" value="Remove" /&amp;gt;&lt;br /&gt;                &amp;lt;HR/&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;div id="selectOneEdit" class="formLineEdit"&amp;gt;&lt;br /&gt;            &amp;lt;div class="editHeader"&amp;gt;&lt;br /&gt;                &amp;lt;b&amp;gt;Select One&amp;lt;/b&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="selectOneSubmit" value="Done" /&amp;gt;&lt;br /&gt;                &amp;lt;input type="submit" name="delete" value="Delete" /&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#labels" target="_blank"&amp;gt;Label&amp;lt;/a&amp;gt;&amp;lt;input name="selectOneLabel" /&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#ids" target="_blank"&amp;gt;ID&amp;lt;/a&amp;gt;&amp;lt;input name="selectOneValue" /&amp;gt;&lt;br /&gt;            &amp;lt;BR/&amp;gt;&amp;lt;input type="submit" name="addSelectOneOption" value="Add Option" /&amp;gt;&lt;br /&gt;            &amp;lt;HR/&amp;gt;&lt;br /&gt;            &amp;lt;div id="selectOneOption" class="selectOption"&amp;gt;&lt;br /&gt;                &amp;lt;i&amp;gt;Option&amp;lt;/i&amp;gt;: &amp;lt;a href="/formHelp#editSelects" target="_blank"&amp;gt;Name&amp;lt;/a&amp;gt;&amp;lt;input name="oneOptionLabel" size=12/&amp;gt;&lt;br /&gt;                &amp;lt;BR/&amp;gt;&amp;lt;a href="/formHelp#editSelects" target="_blank"&amp;gt;Value&amp;lt;/a&amp;gt;&amp;lt;input name="oneOptionValue" size=12/&amp;gt;&lt;br /&gt;                &amp;lt;BR/&amp;gt;&amp;lt;input type="submit" name="removeSelectOneOption" value="Remove" /&amp;gt;&lt;br /&gt;                &amp;lt;HR/&amp;gt;&lt;br /&gt;            &amp;lt;/div&amp;gt;&lt;br /&gt;        &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt; &amp;lt;!-- details--&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt; &amp;lt;!--columns--&amp;gt;&lt;br /&gt;&amp;lt;P/&amp;gt;&lt;br /&gt;&amp;lt;div class="footers"&amp;gt;&lt;br /&gt;    &amp;lt;div id="formName" class="footer first"&amp;gt;&amp;lt;b&amp;gt;Form Name&amp;lt;/b&amp;gt;: &amp;lt;input name="formName" value="default" size=10 length=64 /&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="formVersionNumber" class="footer second"&amp;gt;&lt;br /&gt;        &amp;lt;b&amp;gt;Version Number&amp;lt;/b&amp;gt;:&lt;br /&gt;        &amp;lt;input name="formVersion"&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div id="formSubmitButton" class="footer last"&amp;gt;&amp;lt;input type="submit" name="saveForm" value="Finished - Save Form!" /&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Pretty slick, eh?&lt;br /&gt;&lt;br /&gt;What you do is, you drag form elements from the first column into the second column, where clones are inserted. (That way the element stays in the first column, so you can have an arbitrary number of instances in the second column.) One key thing to note is that clones do not inherit the clonee's bindings, so you have to bind any events after they're created, as shown above in the "stop" event of the "sortable" definition.&lt;br /&gt;&lt;br /&gt;I would try to explain more, but either you already know JQuery reasonably well, in which case the above is probably pretty clear already, or you don't, in which case it will be Greek. So - just copy and paste the above as HTML, launch it in a browser, and play around with it; it should drag and drop right out of the box.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-8143354814246729599?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/8143354814246729599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/10/jquery-how-do-i-love-thee.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/8143354814246729599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/8143354814246729599'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/10/jquery-how-do-i-love-thee.html' title='jQuery, how do I love thee?'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-8132762026104577786</id><published>2009-09-29T20:39:00.000-07:00</published><updated>2009-09-29T21:06:11.962-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='UITableViewCell'/><category scheme='http://www.blogger.com/atom/ns#' term='custom'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='UILabel'/><category scheme='http://www.blogger.com/atom/ns#' term='Settings'/><category scheme='http://www.blogger.com/atom/ns#' term='UITableViewController'/><category scheme='http://www.blogger.com/atom/ns#' term='UIActionSheetDelegate'/><category scheme='http://www.blogger.com/atom/ns#' term='Map'/><category scheme='http://www.blogger.com/atom/ns#' term='UISwitch'/><category scheme='http://www.blogger.com/atom/ns#' term='UIActionSheet'/><category scheme='http://www.blogger.com/atom/ns#' term='UITextField'/><category scheme='http://www.blogger.com/atom/ns#' term='NSUserDefaults'/><category scheme='http://www.blogger.com/atom/ns#' term='outliers'/><category scheme='http://www.blogger.com/atom/ns#' term='UIButton'/><title type='text'>little bits of context-free iPhone code</title><content type='html'>What it says on the label.&lt;br /&gt;&lt;br /&gt;First, an example of how to handle Settings in an iPhone app. The way I do it is, I have a "Settings" class, with lots of class get-and-set methods, so at any time you can just call "[Settings getLanguage]"; then, I have a SettingsViewController, to change them. (Yes, you can register them to be changed in the iPhone's Settings app, but since you have to leave your app for that, this is annoying.)&lt;br /&gt;&lt;br /&gt;Best of all, you don't have to use Core Data. Instead you can use the even simpler NSUserDefault class, like so:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@interface Settings : NSObject {&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;+(int)getListMax;&lt;br /&gt;+(void) setListMax:(int)value;&lt;br /&gt;+(NSString*)getLanguage;&lt;br /&gt;+(void) setLanguage:(NSString*)language;&lt;br /&gt;+(BOOL) doDownload;&lt;br /&gt;+(void) setDoDownload:(BOOL)yesno;&lt;br /&gt;&lt;br /&gt;@implementation Settings&lt;br /&gt;&lt;br /&gt;//This method does all the work&lt;br /&gt;+(id) getSettingFor:(NSString*)key withDefault:(id)defaultValue {&lt;br /&gt; NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];&lt;br /&gt; id value = [defaults objectForKey:key];&lt;br /&gt; if (value==nil) {&lt;br /&gt;  [defaults setObject:defaultValue forKey:key];&lt;br /&gt;  return defaultValue;&lt;br /&gt; }&lt;br /&gt; return value;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(int) getListMax {&lt;br /&gt; return [[Settings getSettingFor:@"listMax" withDefault:[NSNumber numberWithInt:500]] intValue];&lt;br /&gt;}&lt;br /&gt;+(void) setListMax:(int)value {&lt;br /&gt; [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithInt:value] forKey:@"listMax"];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(NSString*) getLanguage {&lt;br /&gt; return [Settings getSettingFor:@"language" withDefault:@"en"];&lt;br /&gt;}&lt;br /&gt;+(void) setLanguage:(NSString*)language {&lt;br /&gt; [[NSUserDefaults standardUserDefaults] setObject:language forKey:@"language"];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(BOOL) doDownload {&lt;br /&gt; return [[Settings getSettingFor:@"doDownload" withDefault:[NSNumber numberWithBool:YES]] boolValue];&lt;br /&gt;}&lt;br /&gt;+(void) setDoDownload:(BOOL)yesno {&lt;br /&gt; [[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:yesno] forKey:@"doDownload"];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...no dealloc, as we never create an instance]&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Note that you easily lazy-initialize all your defaults in code, and then let the user overwrite them. How, you ask? Via the SettingsViewController. Which you could create using the Layout Manager; but I prefer to do it programmatically, with a TableViewController, as it looks slicker much easier to add a Setting that way. Also, it lets me show you an example of my TableViewCell pattern. And while we're at it, a UIActionSheet example too. Voila:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@implementation SettingsViewController&lt;br /&gt;&lt;br /&gt;- (void)viewDidLoad {&lt;br /&gt;    [super viewDidLoad];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;//Note that we save the settings as we leave the view&lt;br /&gt;- (void)viewDidDisappear:(BOOL)animated {&lt;br /&gt; [super viewDidDisappear:animated];&lt;br /&gt; [[NSUserDefaults standardUserDefaults] synchronize];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark TableView methods&lt;br /&gt;&lt;br /&gt;- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {&lt;br /&gt; return 1;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {&lt;br /&gt; //# of settings&lt;br /&gt; return 3;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {&lt;br /&gt; if (indexPath.row==0) { //list max&lt;br /&gt;  LabelTextFieldCell *cell = (LabelTextFieldCell *)[tableView dequeueReusableCellWithIdentifier:@"LabelTextField"];&lt;br /&gt;  if (cell == nil)&lt;br /&gt;   cell = [[[LabelTextFieldCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"LabelTextField"] autorelease];&lt;br /&gt;  &lt;br /&gt;  cell.fieldName = @"listMax";&lt;br /&gt;  cell.fieldDisplayName.text = @"List Max";&lt;br /&gt;  cell.fieldValue.tag=indexPath.row;&lt;br /&gt;  cell.fieldValue.keyboardType = UIKeyboardTypeURL;&lt;br /&gt;  cell.fieldValue.autocapitalizationType = UITextAutocapitalizationTypeNone;&lt;br /&gt;  cell.fieldValue.delegate=self;&lt;br /&gt;  cell.fieldValue.text=[[NSNumber numberWithInt:[Settings getListMax]] stringValue];&lt;br /&gt; }&lt;br /&gt; else if (indexPath.row==1) { // do download&lt;br /&gt;  SwitchCell *cell = (SwitchCell *)[tableView dequeueReusableCellWithIdentifier:@"Switch"];&lt;br /&gt;  if (cell == nil)&lt;br /&gt;   cell = [[[SwitchCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Switch"] autorelease];&lt;br /&gt;&lt;br /&gt;  cell.fieldName = @"doDownload";&lt;br /&gt;  cell.fieldDisplayName.text = @"Download pages";&lt;br /&gt;  cell.mySwitch.tag=indexPath.row;&lt;br /&gt;  cell.mySwitch.on=[Settings doDownload];&lt;br /&gt;  cell.currentController = self; //we do this so we can respond to changes&lt;br /&gt; }&lt;br /&gt; else if (indexPath.row==3) { //language&lt;br /&gt;  ButtonCell *cell = (ButtonCell *)[tableView dequeueReusableCellWithIdentifier:@"Button"];&lt;br /&gt;  if (cell == nil)&lt;br /&gt;   cell = [[[ButtonCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Button"] autorelease];&lt;br /&gt;&lt;br /&gt;  cell.fieldName=@"language";&lt;br /&gt;  cell.fieldDisplayName.text=@"Language";&lt;br /&gt;  cell.button.tag=indexPath.row;&lt;br /&gt;  [cell.button setTitle:[Settings getLanguageName] forState:UIControlStateNormal];&lt;br /&gt;  [cell.button addTarget:self action:@selector(changeLanguage) forControlEvents:UIControlEventTouchUpInside];&lt;br /&gt; }&lt;br /&gt; else&lt;br /&gt;  return nil;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark UITextFieldDelegate&lt;br /&gt;&lt;br /&gt;- (BOOL)textFieldShouldReturn:(UITextField *)textField {&lt;br /&gt; [textField resignFirstResponder];&lt;br /&gt; return YES;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)textFieldDidEndEditing:(UITextField *)textField {&lt;br /&gt; if (textField.tag==0) {&lt;br /&gt;  //TODO: check that it's a valid int!&lt;br /&gt;  [Settings setListMax:[textField.text intValue]];&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Switch&lt;br /&gt;&lt;br /&gt;-(void) valueChanged:(UISwitch*)aSwitch {&lt;br /&gt; if (aSwitch.tag==1) {&lt;br /&gt;  [Settings setDoDownload:aSwitch.on];&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Button&lt;br /&gt;&lt;br /&gt;-(void) changeLanguage {&lt;br /&gt; UIActionSheet *action = [[UIActionSheet alloc]&lt;br /&gt;        initWithTitle:@"Select Language"&lt;br /&gt;        delegate:self&lt;br /&gt;        cancelButtonTitle:nil &lt;br /&gt;        destructiveButtonTitle:nil&lt;br /&gt;        otherButtonTitles:nil];&lt;br /&gt; &lt;br /&gt; for (NSString* language in [Util getValidLanguageNames]) {&lt;br /&gt;  [action addButtonWithTitle:language];&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; [action showInView:self.view];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark UIActionSheetDelegate&lt;br /&gt;&lt;br /&gt;- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {&lt;br /&gt; [Settings setLanguage:[[Util getValidLanguageValues] objectAtIndex:buttonIndex]];&lt;br /&gt; &lt;br /&gt; UIButton *button = (UIButton*) [self.tableView viewWithTag:6];&lt;br /&gt; [button setTitle:[actionSheet buttonTitleAtIndex:buttonIndex] forState:UIControlStateNormal];&lt;br /&gt; &lt;br /&gt; [actionSheet dismissWithClickedButtonIndex:buttonIndex animated:YES];&lt;br /&gt; [actionSheet release];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...dealloc etc...]&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;That's pretty straightforward; build a table view, populate it with table view cells, and respond to the actions in the cells. But how do the cells work? Well, first look at their (theoretically abstract) common parent superclass, ITRTableViewCell:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@interface ITRTableViewCell : UITableViewCell {&lt;br /&gt; NSString *fieldName;&lt;br /&gt; UILabel *fieldDisplayName;&lt;br /&gt; UIViewController *currentController;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) NSString *fieldName;&lt;br /&gt;@property (nonatomic, retain) UILabel *fieldDisplayName;&lt;br /&gt;@property (nonatomic, assign) UIViewController *currentController; //weak reference&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@implementation ITRTableViewCell&lt;br /&gt;&lt;br /&gt;@synthesize fieldName, fieldDisplayName, currentController;&lt;br /&gt;&lt;br /&gt;-(id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {&lt;br /&gt; [super initWithFrame:frame reuseIdentifier:reuseIdentifier];&lt;br /&gt; &lt;br /&gt; self.fieldDisplayName = [[UILabel alloc] init];&lt;br /&gt; fieldDisplayName.textAlignment = UITextAlignmentLeft;&lt;br /&gt; fieldDisplayName.adjustsFontSizeToFitWidth=YES;&lt;br /&gt; fieldDisplayName.minimumFontSize=8;&lt;br /&gt; fieldDisplayName.numberOfLines=2;&lt;br /&gt; fieldDisplayName.font = [UIFont systemFontOfSize:14];&lt;br /&gt; &lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...dealloc etc...]&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;which will make more sense when you look at its use in a subclass:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@interface LabelTextFieldCell : ITRTableViewCell {&lt;br /&gt; UITextField *fieldValue;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) UITextField *fieldValue;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@implementation LabelTextFieldCell&lt;br /&gt;&lt;br /&gt;@synthesize fieldValue;&lt;br /&gt;&lt;br /&gt;-(id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {&lt;br /&gt; [super initWithFrame:frame reuseIdentifier:reuseIdentifier];&lt;br /&gt; &lt;br /&gt; [self.contentView addSubview:fieldDisplayName];&lt;br /&gt; &lt;br /&gt; self.fieldValue = [[UITextField alloc] init];&lt;br /&gt; fieldValue.clearsOnBeginEditing = NO;&lt;br /&gt; fieldValue.enablesReturnKeyAutomatically = YES;&lt;br /&gt; fieldValue.returnKeyType = UIReturnKeyNext;&lt;br /&gt; fieldValue.autocapitalizationType = UITextAutocapitalizationTypeSentences;&lt;br /&gt; fieldValue.autocorrectionType = UITextAutocorrectionTypeNo;&lt;br /&gt; fieldValue.enablesReturnKeyAutomatically = YES;&lt;br /&gt; fieldValue.backgroundColor = [self getBackgroundColor];&lt;br /&gt; fieldValue.textColor = [self getTextColor];&lt;br /&gt; fieldValue.textAlignment = UITextAlignmentLeft;&lt;br /&gt; fieldValue.font = [UIFont systemFontOfSize:14];&lt;br /&gt; fieldValue.borderStyle = UITextBorderStyleBezel;&lt;br /&gt; [self.contentView addSubview:fieldValue];&lt;br /&gt; &lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void)layoutSubviews {&lt;br /&gt; [super layoutSubviews];&lt;br /&gt; CGRect contentRect = self.contentView.bounds;&lt;br /&gt; CGFloat boundsX = contentRect.origin.x;&lt;br /&gt; CGRect frame;&lt;br /&gt; &lt;br /&gt; frame = CGRectMake(boundsX+10, 0, 140, 30);&lt;br /&gt; fieldDisplayName.frame = frame;&lt;br /&gt; &lt;br /&gt; frame = CGRectMake(boundsX+150, 0, 160, 30);&lt;br /&gt; fieldValue.frame = frame;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...dealloc...]&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Ya see? The superclass defines the name on the left; the subclass defines the input on the right with which the user interacts. Here are the ButtonCell and SwitchCell implementations:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;@implementation ButtonCell&lt;br /&gt;&lt;br /&gt;@synthesize button;&lt;br /&gt;&lt;br /&gt;-(id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {&lt;br /&gt; [super initWithFrame:frame reuseIdentifier:reuseIdentifier];&lt;br /&gt; &lt;br /&gt; [self.contentView addSubview:fieldDisplayName];&lt;br /&gt; &lt;br /&gt; self.button = [UIButton buttonWithType:UIButtonTypeRoundedRect];&lt;br /&gt; button.backgroundColor = [self getBackgroundColor];&lt;br /&gt; button.titleLabel.textColor = [self getTextColor];&lt;br /&gt; button.titleLabel.textAlignment = UITextAlignmentLeft;&lt;br /&gt; button.titleLabel.font = [UIFont systemFontOfSize:14]; &lt;br /&gt; [self.contentView addSubview:button];&lt;br /&gt; &lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void)layoutSubviews {&lt;br /&gt; [super layoutSubviews];&lt;br /&gt; CGRect contentRect = self.contentView.bounds;&lt;br /&gt; CGFloat boundsX = contentRect.origin.x;&lt;br /&gt; CGRect frame;&lt;br /&gt; &lt;br /&gt; frame = CGRectMake(boundsX+10, 0, 140, 30);&lt;br /&gt; fieldDisplayName.frame = frame;&lt;br /&gt; &lt;br /&gt; frame = CGRectMake(boundsX+150, 0, 160, 30);&lt;br /&gt; button.frame = frame;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...dealloc...]&lt;br /&gt;&lt;br /&gt;@implementation SwitchCell&lt;br /&gt;&lt;br /&gt;@synthesize mySwitch;&lt;br /&gt;&lt;br /&gt;-(id)initWithFrame:(CGRect)frame reuseIdentifier:(NSString *)reuseIdentifier {&lt;br /&gt; [super initWithFrame:frame reuseIdentifier:reuseIdentifier];&lt;br /&gt; &lt;br /&gt; [self.contentView addSubview:fieldDisplayName];&lt;br /&gt; &lt;br /&gt; self.mySwitch = [[UISwitch alloc] init];&lt;br /&gt; mySwitch.backgroundColor = [self getBackgroundColor];&lt;br /&gt; [mySwitch addTarget:self action:@selector(switchAction:) forControlEvents:UIControlEventValueChanged];&lt;br /&gt; [self.contentView addSubview:mySwitch];&lt;br /&gt; &lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void)layoutSubviews {&lt;br /&gt; [super layoutSubviews];&lt;br /&gt; CGRect contentRect = self.contentView.bounds;&lt;br /&gt; CGFloat boundsX = contentRect.origin.x;&lt;br /&gt; CGRect frame;&lt;br /&gt; &lt;br /&gt; frame = CGRectMake(boundsX+10, 0, 140, 30);&lt;br /&gt; fieldDisplayName.frame = frame;&lt;br /&gt; &lt;br /&gt; frame = CGRectMake(boundsX+210, 0, 110, 30);&lt;br /&gt; mySwitch.frame = frame;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void)switchAction:(UISwitch*) sender {&lt;br /&gt; [currentController performSelector:@selector(valueChanged:) withObject: sender];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;[...dealloc...]&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Note the callbacker on the SwitchCell, not necessary on ButtonCell because we can set its target action when we create it.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;There you go: a Settings architecture, and a programmatic TableViewController with a useful custom TableViewCell architecture and three examples of same. Use as you like.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And now, an actual algorithm I wrote. An algorithm! I know! Real programming! Hence my pride. The problem was, given a list of points that might have a few spurious outliers, get rid of those outliers and build a zoomed-in region that excludes them, to show in an MKMapView. My solution, in a class called Locator:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;+(MKCoordinateRegion)getRegionForAnnotations:(NSArray*)annotations {&lt;br /&gt;&lt;br /&gt; NSMutableArray *latitudes = [NSMutableArray arrayWithCapacity:[annotations count]];&lt;br /&gt; NSMutableArray *longitudes = [NSMutableArray arrayWithCapacity:[annotations count]];&lt;br /&gt;&lt;br /&gt; for (id &lt;MKAnnotation&gt; entry in annotations) {&lt;br /&gt;  double myLat = [entry coordinate].latitude;&lt;br /&gt;  double myLong = [entry coordinate].longitude;&lt;br /&gt;  [latitudes addObject:[NSNumber numberWithDouble:myLat]];&lt;br /&gt;  [longitudes addObject:[NSNumber numberWithDouble:myLong]];&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; //OK, we've got the box that includes *all* the annotations&lt;br /&gt; //Now we get rid of outliers aka bad address finds.&lt;br /&gt; NSArray *sortedLatitudes = [latitudes sortedArrayUsingSelector:@selector(compare:)];&lt;br /&gt; NSArray *sortedLongitudes = [longitudes sortedArrayUsingSelector:@selector(compare:)];&lt;br /&gt; &lt;br /&gt; NSRange latLongest = [Locator getLongestContiguousRangeIn:sortedLatitudes];&lt;br /&gt; NSRange lonLongest = [Locator getLongestContiguousRangeIn:sortedLongitudes];&lt;br /&gt; &lt;br /&gt; NSNumber* minLat = [sortedLatitudes objectAtIndex:latLongest.location];&lt;br /&gt; NSNumber* maxLat = [sortedLatitudes objectAtIndex:latLongest.location+latLongest.length-1];&lt;br /&gt; NSNumber* minLon = [sortedLongitudes objectAtIndex:lonLongest.location];&lt;br /&gt; NSNumber* maxLon = [sortedLongitudes objectAtIndex:lonLongest.location+lonLongest.length-1];&lt;br /&gt; MKCoordinateRegion regionToShow = [Locator getRegionForMinLat:minLat minLong:minLon maxLat:maxLat maxLong:maxLon];&lt;br /&gt; return regionToShow;&lt;br /&gt;}&lt;br /&gt;  &lt;br /&gt;+(NSRange) getLongestContiguousRangeIn:(NSArray*)numbers {&lt;br /&gt; NSRange longest; longest.location=0; longest.length=0;&lt;br /&gt; NSRange range; range.location=0; range.length=0;&lt;br /&gt; for (int i=0; i&lt;[numbers count]; i++) {&lt;br /&gt;  double thisLat = [[numbers objectAtIndex:i] doubleValue];&lt;br /&gt;  double lastLat = i&gt;0 ? [[numbers objectAtIndex:i-1] doubleValue] : thisLat;&lt;br /&gt;  if (thisLat-lastLat&lt;=(double)1.0)&lt;br /&gt;   range.length++;&lt;br /&gt;  else {&lt;br /&gt;   range.location=i;&lt;br /&gt;   range.length=0;&lt;br /&gt;  }&lt;br /&gt;  if (range.length&gt;longest.length)&lt;br /&gt;   longest=range;&lt;br /&gt; }&lt;br /&gt; return longest;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(MKCoordinateRegion) getRegionForMinLat:(NSNumber*)minLat minLong:(NSNumber*)minLong maxLat:(NSNumber*)maxLat maxLong:(NSNumber*)maxLong {&lt;br /&gt;  CLLocationCoordinate2D regionCenter;&lt;br /&gt;  regionCenter.latitude=([minLat doubleValue]+[maxLat doubleValue])/2;&lt;br /&gt;  regionCenter.longitude=([minLong doubleValue]+[maxLong doubleValue])/2;&lt;br /&gt;  &lt;br /&gt;  MKCoordinateSpan regionSpan;&lt;br /&gt;  regionSpan.latitudeDelta=[maxLat doubleValue]-[minLat doubleValue];&lt;br /&gt;  regionSpan.longitudeDelta=[maxLong doubleValue]-[minLong doubleValue];&lt;br /&gt;  &lt;br /&gt;  MKCoordinateRegion region;&lt;br /&gt;  region.center=regionCenter;&lt;br /&gt;  region.span = regionSpan;&lt;br /&gt;  return region;&lt;br /&gt;}  &lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Quite elegant and efficient, if I do say so myself. It would make for an interesting interview question, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-8132762026104577786?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/8132762026104577786/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/09/little-bits-of-context-free-iphone-code.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/8132762026104577786'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/8132762026104577786'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/09/little-bits-of-context-free-iphone-code.html' title='little bits of context-free iPhone code'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-5201338296743366581</id><published>2009-09-09T17:39:00.001-07:00</published><updated>2009-09-09T17:44:17.282-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='breakpoint'/><category scheme='http://www.blogger.com/atom/ns#' term='XCode'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='inexplicable'/><category scheme='http://www.blogger.com/atom/ns#' term='unit-test'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='weird'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><category scheme='http://www.blogger.com/atom/ns#' term='unit'/><category scheme='http://www.blogger.com/atom/ns#' term='failure'/><title type='text'>Weird, man, weird</title><content type='html'>So I have switched to the &lt;a href="http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting"&gt;Google Toolbox iPhone unit-test framework&lt;/a&gt;, and it mostly works a dream. You plug it in as per those instructions, write some tests, switch to your test target, hit "Build", and it runs your unit tests in the build phase, and highlights test failures in the same way that XCode normally highlights compile errors.&lt;br /&gt;&lt;br /&gt;However. I have found something profoundly weird about it, which cost me a bunch of today.&lt;br /&gt;&lt;br /&gt;Suppose your unit test is failing, and you want to debug it. Well, you can; but to do so, you have to not just Build but Run the test target, so you have to comment out the tests that are causing the build to fail. Easy enough. Then the test target launches normally in the simulator, and you can step through with the debugger, and find out what went wrong.&lt;br /&gt;&lt;br /&gt;However. Guess what? If you leave &lt;i&gt;any breakpoints in your code&lt;/i&gt;, and then go back and hit Build, then the Google unit-test framework will begin to fail, in strange and inexplicable ways (mostly null values where there shouldn't be any, in my case.) Remove those breakpoints - and &lt;i&gt;poof&lt;/i&gt;, your unit tests are working again.&lt;br /&gt;&lt;br /&gt;May you not spend a day beating your head against this, as I did.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-5201338296743366581?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/5201338296743366581/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/09/weird-man-weird.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5201338296743366581'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/5201338296743366581'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/09/weird-man-weird.html' title='Weird, man, weird'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-1944440156908037898</id><published>2009-09-01T09:31:00.000-07:00</published><updated>2009-09-01T09:50:32.574-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='UIImagePickerControllerDelegate'/><category scheme='http://www.blogger.com/atom/ns#' term='SenTestingKit'/><category scheme='http://www.blogger.com/atom/ns#' term='MKMapView'/><category scheme='http://www.blogger.com/atom/ns#' term='ImagePicker'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='UIImagePIckerController'/><category scheme='http://www.blogger.com/atom/ns#' term='MapView'/><category scheme='http://www.blogger.com/atom/ns#' term='presentModalViewController'/><category scheme='http://www.blogger.com/atom/ns#' term='MKAnnotation'/><title type='text'>iPhone bits and bobs</title><content type='html'>Hello there, O my droogs. Long time no talk. I've been working on an iPhone app for pay, you see, which I figure kind of limits how much code I can provide you. I do have a few notes that might be worth sharing, though:&lt;br /&gt;&lt;br /&gt;&lt;UL&gt;&lt;br /&gt;&lt;LI&gt;&lt;b&gt;MapView code&lt;/b&gt;&lt;br /&gt;&lt;P&gt;A fully functional MapViewDelegate implementation for you. All you have to do to start putting items on maps is make them implement MKAnnotation, which is super-easy.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;- (MKAnnotationView *)mapView:(MKMapView *)myMapView viewForAnnotation:(id &lt;MKAnnotation&gt;)annotation {&lt;br /&gt; &lt;br /&gt; //this is clumsy, but there's no obvious better way - otherwise it crashes when showing user location&lt;br /&gt; if (![annotation isKindOfClass:[ECEntry class]])&lt;br /&gt;  return nil;&lt;br /&gt;&lt;br /&gt; ECEntry *myEntry = (ECEntry*)annotation;&lt;br /&gt; &lt;br /&gt; MKPinAnnotationView *myView = (MKPinAnnotationView*)[myMapView dequeueReusableAnnotationViewWithIdentifier:@"Local"];&lt;br /&gt; if (myView==nil)&lt;br /&gt;  myView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"Local"] autorelease];&lt;br /&gt; myView.pinColor = myEntry.groupID==0 ? MKPinAnnotationColorGreen : MKPinAnnotationColorRed;&lt;br /&gt; myView.canShowCallout=YES;&lt;br /&gt; myView.rightCalloutAccessoryView = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];&lt;br /&gt; return myView;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)mapView:(MKMapView *)mapView annotationView:(MKAnnotationView *)view calloutAccessoryControlTapped:(UIControl *)control {&lt;br /&gt; ECEntryViewController *entryViewController = [[ECEntryViewController alloc] initWithNibName:nil bundle:nil];&lt;br /&gt; entryViewController.myEntry = (ECEntry*)[view annotation];&lt;br /&gt; [self.navigationController pushViewController:entryViewController animated:YES];&lt;br /&gt; [entryViewController release];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;LI&gt;&lt;b&gt;Using the camera via UIImagePickerController&lt;/b&gt;&lt;br /&gt;&lt;P&gt;So this is amazingly easy, and I can even provide you with some code that&lt;br /&gt;a) launches a camera, phot-album selector, or photo-library selector, depending on what's available on your device:&lt;br /&gt;b) presents the image picker as a modal view controller, keeping it within your existing NavigationController tree:&lt;br /&gt;c) gets the selected/captured image (via UIImagePickerControllerDelegate) and saves it to a file.&lt;br /&gt; &lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void) doCamera {&lt;br /&gt; BOOL hasCamera = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];&lt;br /&gt; BOOL hasAlbum = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum];&lt;br /&gt; BOOL hasLibrary = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary];&lt;br /&gt;&lt;br /&gt; if (hasCamera || hasAlbum || hasLibrary) {&lt;br /&gt;  UIImagePickerController *photoController = [[UIImagePickerController alloc] init];&lt;br /&gt;  photoController.delegate = self;&lt;br /&gt;&lt;br /&gt;  if (hasCamera)&lt;br /&gt;   photoController.sourceType = UIImagePickerControllerSourceTypeCamera;&lt;br /&gt;  else if (hasAlbum) {&lt;br /&gt;   photoController.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;&lt;br /&gt;   NSString *warningMessage = [NSString localizedStringWithFormat:@"Your device has no camera, but does have an album of saved photos to choose from"];&lt;br /&gt;   [Util doAlert:NSLocalizedString(@"Warning",@"Alert title") withMessage:warningMessage];&lt;br /&gt;  }&lt;br /&gt;  else if (hasLibrary) {&lt;br /&gt;   photoController.sourceType = UIImagePickerControllerSourceTypePhotoLibrary;&lt;br /&gt;   NSString *warningMessage = [NSString localizedStringWithFormat:@"Your device has no camera, but does have a photo library to choose from"];&lt;br /&gt;   [Util doAlert:NSLocalizedString(@"Warning",@"Alert title") withMessage:warningMessage];&lt;br /&gt;  }&lt;br /&gt;  &lt;br /&gt;  [self doSave];&lt;br /&gt;  [self presentModalViewController:photoController animated:YES];&lt;br /&gt;  [photoController release];&lt;br /&gt; }&lt;br /&gt; else {&lt;br /&gt;  NSString *alertMessage = [NSString localizedStringWithFormat:@"Your device does not support photos"];&lt;br /&gt;  [Util doAlert:NSLocalizedString(@"No photo support",@"Alert title") withMessage:alertMessage];&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {&lt;br /&gt;&lt;br /&gt; UIImage *image = [info objectForKey:UIImagePickerControllerEditedImage];&lt;br /&gt; if (image == nil)&lt;br /&gt;  image = [info objectForKey:UIImagePickerControllerOriginalImage];&lt;br /&gt;&lt;br /&gt; //TODO, if possible: if the image has already been saved, just use its path, don't save it again&lt;br /&gt;&lt;br /&gt; if (image!=nil) {&lt;br /&gt;  //convert image to NSData - use Settings for PNG/JPG&lt;br /&gt;  [Util showActivity];&lt;br /&gt;  NSData *imageData=nil;&lt;br /&gt;  if ([Settings saveImagesAsPNG])&lt;br /&gt;   imageData = [NSData dataWithData:UIImagePNGRepresentation(image)];&lt;br /&gt;  else&lt;br /&gt;   imageData = [NSData dataWithData:UIImageJPEGRepresentation(image, [Settings getJPEGQuality])];&lt;br /&gt; &lt;br /&gt;  //save NSData to file&lt;br /&gt;  NSString *pathDir = [Util getApplicationDocumentsPath]; &lt;br /&gt;  NSString *fileName = [[NSProcessInfo processInfo] globallyUniqueString];&lt;br /&gt;  fileName = [fileName stringByAppendingString:([Settings saveImagesAsPNG] ? @".png" : @".jpg")];&lt;br /&gt;  NSString *fullPath = [pathDir stringByAppendingPathComponent:fileName];&lt;br /&gt;&lt;br /&gt;  NSError *error=nil;&lt;br /&gt;  [imageData writeToFile:fullPath options:NSAtomicWrite error:&amp;error];&lt;br /&gt;  if (error) {&lt;br /&gt;   NSString *errorMessage = [NSString localizedStringWithFormat:@"Unable to write to file %@: %@ - %@", fullPath, error, [error userInfo]];&lt;br /&gt;   [Util doAlert:NSLocalizedString(@"Error",@"Alert title") withMessage:errorMessage];&lt;br /&gt;  }&lt;br /&gt;  [Util stopShowingActivity];&lt;br /&gt; &lt;br /&gt;  //set entry&lt;br /&gt;  myEntry.photoPath=fileName;&lt;br /&gt;&lt;br /&gt;  //all done&lt;br /&gt;  [self dismissModalViewControllerAnimated:YES];&lt;br /&gt;  [self.tableView reloadData];&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {&lt;br /&gt; [self dismissModalViewControllerAnimated:YES];&lt;br /&gt; [self.tableView reloadData];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;&lt;LI&gt;&lt;b&gt;Unit test woes&lt;/b&gt;&lt;br /&gt;&lt;P&gt;I've been having loads of trouble with the allegedly-baked-in unit-testing framework described &lt;a href="http://developer.apple.com/IPhone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html"&gt;here&lt;/a&gt;. Specifically, I get masses of "symbol(s) not found" linker errors when I try to compile for the Testing target, re all kinds of basic UIKit and CoreData stuff. I presume it's some kind of importing-the-wrong-kind-of-framework problem, but I have no idea how to correct it, and I don't want to dive into the seething nest of rattlesnakes that is the innards of XCode's build process. Should I just switch to the &lt;a href="http://code.google.com/p/google-toolbox-for-mac/wiki/iPhoneUnitTesting"&gt;Google unit-test framework for iPhone&lt;/a&gt;?&lt;br /&gt;&lt;/UL&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-1944440156908037898?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/1944440156908037898/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/09/iphone-bits-and-bobs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/1944440156908037898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/1944440156908037898'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/09/iphone-bits-and-bobs.html' title='iPhone bits and bobs'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-7660828982659923765</id><published>2009-08-10T15:06:00.000-07:00</published><updated>2009-08-10T15:12:12.027-07:00</updated><title type='text'>down in the metal</title><content type='html'>I just wanted to say that I didn't think I would ever write low-level C code again. And yet, here I am:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;+(NSNumber *) getRandomDoubleMin:(double)min max:(double)max {&lt;br /&gt; srandomdev();&lt;br /&gt; int nRange = (int) max-min;&lt;br /&gt; int nRand = random() % nRange;&lt;br /&gt; double dRand = (double) nRand;&lt;br /&gt; dRand = dRand+min;&lt;br /&gt; int nRand2 = random() %10000;&lt;br /&gt; double dRand2 = (double)nRand2;&lt;br /&gt; dRand2=dRand2/10000;&lt;br /&gt; dRand=dRand+dRand2;&lt;br /&gt; return [NSNumber numberWithDouble:dRand];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;(It's just a utility function to help populate a map with an assortment of randomly distributed pushpins, for test purposes. But still. Sheesh.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-7660828982659923765?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/7660828982659923765/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/08/down-in-metal.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7660828982659923765'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7660828982659923765'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/08/down-in-metal.html' title='down in the metal'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-7762661492350150977</id><published>2009-08-03T19:51:00.000-07:00</published><updated>2009-08-03T20:25:05.911-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='XCode'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='tests'/><category scheme='http://www.blogger.com/atom/ns#' term='Apple'/><category scheme='http://www.blogger.com/atom/ns#' term='unit'/><title type='text'>if you love some source code, set it free</title><content type='html'>So I have gone and&lt;UL&gt;&lt;LI&gt;a) officially submitted my Wikitravel-editor iPhone app to the App Store &lt;LI&gt;b) released its source code under a BSD open-source license for all to see and play with, at &lt;a href="http://code.google.com/p/itravelwrite/"&gt;code.google.com/p/itravelwrite&lt;/a&gt;.&lt;/UL&gt;&lt;br /&gt;&lt;br /&gt;To be honest I would have liked another few days for cleanup and further testing, but I've gone and gotten a real job (iPhone app development, again) that will occupy me for at least a couple of months, so I had to hurry things up. I apologize for the ugliness of the code.&lt;br /&gt;&lt;br /&gt;Some of that ugliness is due to its extremely crude testing framework. Unit testing, the panacea of modern software development, is bizarrely difficult to integrate with the iPhone SDK. There are various tools to help you - one created by Google - but they're all far more complex and kludgy than I'd like, especially compared to the elegant joy that is GAEUnit.&lt;br /&gt;&lt;br /&gt;It turns out, however, that it is possible to seamlessly integrate unit tests and test-driven development into iPhone development with XCode. There are a few caveats, but overall, &lt;a href="http://developer.apple.com/IPhone/library/documentation/Xcode/Conceptual/iphone_development/135-Unit_Testing_Applications/unit_testing_applications.html"&gt;these instructions&lt;/a&gt; work very well, if you follow them very carefully.&lt;br /&gt;&lt;br /&gt;The caveat is that you can only run the unit tests on an actual physical device, not in the simulator. Now, in some ways this is actually a bonus - you avoid the "but it works in the simulator!" pitfall, and I have learned that the only way to get a feel for what your UI should be is to work on a real iPhone or iPod Touch, the simulator is disastrously misleading - but it does mean that you'll have to go ahead and shell out the $100 to join the iPhone Developer Program so that you can generate the certificates needed to sign your app and install it on your device.&lt;br /&gt;&lt;br /&gt;Which in turn is a bit of a hassle. But it's a necessary hassle, and one you should get out of the way as soon as possible. "Build early, build often," is a motto that has served me well for many years.&lt;br /&gt;&lt;br /&gt;The device-testing is also limited in that you have to specifically select a test target, so you can't automatically run your unit tests every time you build your app. But as long as you remember to unit-test at least daily, and immediately after making major changes, I can't see this as &lt;i&gt;that&lt;/i&gt; big a shortcoming.&lt;br /&gt;&lt;br /&gt;Here's some code to run tests in their own separate database, so that you don't pollute your application's data.&lt;br /&gt;&lt;br /&gt;Your test case's .h file looks something like this:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;//  Dependent unit tests mean unit test code depends on an application to be injected into.&lt;br /&gt;//  Setting this to 0 means the unit test code is designed to be linked into an independent executable.&lt;br /&gt;#define USE_DEPENDENT_UNIT_TEST 1&lt;br /&gt;&lt;br /&gt;#import &amp;lt;SenTestingKit/SenTestingKit.h&amp;gt;&lt;br /&gt;#import &amp;lt;UIKit/UIKit.h&amp;gt;&lt;br /&gt;#import "MyAppDelegate.h"&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;@interface MyTestCase : SenTestCase {&lt;br /&gt;    NSManagedObjectContext *context;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and its .m:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void) setUp {&lt;br /&gt;    MyAppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];&lt;br /&gt;    NSURL *storeUrl = [NSURL fileURLWithPath: [[appDelegate applicationDocumentsDirectory] stringByAppendingPathComponent: @"MyAppTest.sqlite"]];&lt;br /&gt;&lt;br /&gt; NSError *error;&lt;br /&gt;    NSPersistentStoreCoordinator *persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [appDelegate managedObjectModel]];&lt;br /&gt;    if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:nil error:&amp;error]) {&lt;br /&gt;        // Handle error&lt;br /&gt;  NSLog(@"Error creating persistent store MyAppTest.sqlite %@, %@", error, [error userInfo]);&lt;br /&gt;    }&lt;br /&gt; context = [[NSManagedObjectContext alloc] init];&lt;br /&gt; [context setPersistentStoreCoordinator: persistentStoreCoordinator]; &lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void) tearDown {&lt;br /&gt; [context release];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-7762661492350150977?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/7762661492350150977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/08/if-you-love-some-source-code-set-it.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7762661492350150977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7762661492350150977'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/08/if-you-love-some-source-code-set-it.html' title='if you love some source code, set it free'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-7773801928876327755</id><published>2009-07-17T19:24:00.000-07:00</published><updated>2009-07-17T19:45:45.318-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NSThread'/><category scheme='http://www.blogger.com/atom/ns#' term='NSURLConnection'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='selector'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='singleton'/><category scheme='http://www.blogger.com/atom/ns#' term='SDK'/><category scheme='http://www.blogger.com/atom/ns#' term='NSRequest'/><category scheme='http://www.blogger.com/atom/ns#' term='HTTP'/><category scheme='http://www.blogger.com/atom/ns#' term='NSURL'/><category scheme='http://www.blogger.com/atom/ns#' term='HttpHelper'/><category scheme='http://www.blogger.com/atom/ns#' term='POST'/><category scheme='http://www.blogger.com/atom/ns#' term='GET'/><title type='text'>HttpHelper</title><content type='html'>...it's sort of like Hamburger Helper, only tastier.&lt;br /&gt;&lt;br /&gt;It's been a week of tweaking, fixing little bugs, spiralling in towards hoped-for perfection. I'm pleased to report that I now have an alpha-test version of my iPhone app up and running on my iPod Touch, and it looks to work a charm. If all goes very well indeed I might submit it to the App Store - where it will ultimately be available for the low low price of $0.00 - by the end of the month.&lt;br /&gt;&lt;br /&gt;In the meantime, here's a utility class that people might find handy: my HttpHelper singleton. It's not &lt;i&gt;really&lt;/i&gt; a class variable, since it seems Objective-C doesn't support them (Oh, Smalltalk, how I miss your class instance variables) but "static" has the same effect, if you code carefully. Like so:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;//&lt;br /&gt;//  HttpHelper.m&lt;br /&gt;//  iTravelWrite&lt;br /&gt;//&lt;br /&gt;//  Singleton class used to send HTTP requests and forward responses to selectors passed in by the caller.&lt;br /&gt;//  Created by Jon Evans on 04/07/09.&lt;br /&gt;//&lt;br /&gt;&lt;br /&gt;#import "HttpHelper.h"&lt;br /&gt;#import "Util.h"&lt;br /&gt;&lt;br /&gt;@implementation HttpHelper&lt;br /&gt;&lt;br /&gt;static HttpHelper *singleton=nil;&lt;br /&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Singleton methods&lt;br /&gt;&lt;br /&gt;+(HttpHelper *) getInstance {&lt;br /&gt; if (singleton==nil)&lt;br /&gt;  singleton = [[HttpHelper alloc] init];&lt;br /&gt; return singleton;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(id)allocWithZone:(NSZone *)zone {&lt;br /&gt; if (singleton == nil) {&lt;br /&gt;  singleton = [super allocWithZone:zone];&lt;br /&gt;  return singleton;&lt;br /&gt; }&lt;br /&gt; return nil;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(id)copyWithZone:(NSZone *)zone {&lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(id)retain {&lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(unsigned)retainCount {&lt;br /&gt; return UINT_MAX;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(void)release {&lt;br /&gt; //pass&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(id)autorelease {&lt;br /&gt; return self;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)dealloc {&lt;br /&gt;    [super dealloc];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;All the above is housekeeping stuff to ensure that we only ever have one instance of an HttpHelper. Not that a duplicate would be so disastrous in this case, but hey, if you're writing a singleton, write a singleton, right?&lt;br /&gt;&lt;br /&gt;Here's the part where it actually does stuff. In particular, it does all the HTTP GETs and HTTP POSTs that your iPhone app will ever need:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Business logic&lt;br /&gt;&lt;br /&gt;+(NSURLRequest*) buildRequestWithPostKeys:(NSArray *) postKeys postValues:(NSArray *) postValues urlString:(NSString *)urlString {&lt;br /&gt; &lt;br /&gt; NSMutableString *params=[[NSMutableString alloc] initWithCapacity:1024];&lt;br /&gt; for (int i=0; i&lt;[postValues count]; i++) {&lt;br /&gt;  [params appendString:[postKeys objectAtIndex:i]];&lt;br /&gt;  [params appendString:@"="];&lt;br /&gt;  [params appendString:[postValues objectAtIndex:i]];&lt;br /&gt;  [params appendString:@"&amp;"];&lt;br /&gt; }&lt;br /&gt; NSData * paramData = [params dataUsingEncoding:NSUTF8StringEncoding];&lt;br /&gt; &lt;br /&gt; NSURL * url = [NSURL URLWithString:urlString];&lt;br /&gt; NSURLRequestCachePolicy policy = NSURLRequestReloadIgnoringCacheData; // never cache a post response, at least in my app&lt;br /&gt; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url cachePolicy:policy timeoutInterval:10.0];&lt;br /&gt; &lt;br /&gt; NSString *msgLength = [NSString stringWithFormat:@"%d", [paramData length]];&lt;br /&gt; [request addValue: msgLength forHTTPHeaderField:@"Content-Length"];&lt;br /&gt; [request setHTTPMethod:@"POST"];&lt;br /&gt; [request setHTTPBody: paramData];&lt;br /&gt; return request;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(BOOL) doPost:(NSURLRequest *)request forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure {&lt;br /&gt; &lt;br /&gt; NSArray *keys = [NSArray arrayWithObjects:@"request", @"caller", @"onSuccess", @"onFailure", nil];&lt;br /&gt; NSArray *values = [NSArray arrayWithObjects:request, caller,&lt;br /&gt;        [NSValue valueWithBytes:&amp;onSuccess objCType:@encode(SEL)],&lt;br /&gt;        [NSValue valueWithBytes:&amp;onFailure objCType:@encode(SEL)],&lt;br /&gt;        nil];&lt;br /&gt; NSDictionary *args = [NSDictionary dictionaryWithObjects:values forKeys: keys];&lt;br /&gt; NSThread* uploadThread = [[NSThread alloc] initWithTarget:[self getInstance] selector:@selector(doHttp:) object:args];&lt;br /&gt; [uploadThread start];&lt;br /&gt; [uploadThread release];&lt;br /&gt; return TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(NSURLRequestCachePolicy)getCachePolicyFor:(NSString *)urlString {&lt;br /&gt; /*&lt;br /&gt;  Hived out to a separate method because we might fine-tune this later.&lt;br /&gt;  &lt;br /&gt;  In theory, this will cause the app to use the cache policies set by&lt;br /&gt;  wetravelwrite.appspot.com, which as of this writing means 1 hour&lt;br /&gt;  for listing information, and 10 hours for searches.&lt;br /&gt;  &lt;br /&gt;  In the ListingEditController and ListingsViewController we manually wipe&lt;br /&gt;  the cache for listings edited by the app.&lt;br /&gt; */&lt;br /&gt;// return NSURLRequestUseProtocolCachePolicy;&lt;br /&gt; return NSURLRequestReloadIgnoringCacheData;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(NSURLRequest *)getURLRequestFor:(NSString *)urlString {&lt;br /&gt; NSURL *url = [NSURL URLWithString:urlString];&lt;br /&gt; NSURLRequestCachePolicy policy = [self getCachePolicyFor:urlString];&lt;br /&gt; NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:policy timeoutInterval:7.5];&lt;br /&gt; return request;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;+(BOOL) doGet:(NSString *)urlString forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure {&lt;br /&gt; NSURLRequest *request = [self getURLRequestFor:urlString];&lt;br /&gt; NSArray *keys = [NSArray arrayWithObjects:@"request", @"caller", @"onSuccess", @"onFailure", nil];&lt;br /&gt; NSArray *values = [NSArray arrayWithObjects:request, caller,&lt;br /&gt;        [NSValue valueWithBytes:&amp;onSuccess objCType:@encode(SEL)],&lt;br /&gt;        [NSValue valueWithBytes:&amp;onFailure objCType:@encode(SEL)],&lt;br /&gt;        nil];&lt;br /&gt; NSDictionary *args = [NSDictionary dictionaryWithObjects:values forKeys: keys];&lt;br /&gt; NSThread* uploadThread = [[NSThread alloc] initWithTarget:[self getInstance] selector:@selector(doHttp:) object:args];&lt;br /&gt; [uploadThread start];&lt;br /&gt; [uploadThread release];&lt;br /&gt; return TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;-(BOOL) doHttp:(NSDictionary *)args&lt;br /&gt; {&lt;br /&gt; @synchronized (self) {&lt;br /&gt;  //autorelease pool&lt;br /&gt;  NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];&lt;br /&gt;  &lt;br /&gt;  NSURLRequest *request = [args objectForKey:@"request"];&lt;br /&gt;  NSObject *caller = [args objectForKey:@"caller"];&lt;br /&gt;   &lt;br /&gt;  SEL onSuccess;&lt;br /&gt;  [[args objectForKey:@"onSuccess"] getValue:&amp;onSuccess];&lt;br /&gt;  SEL onFailure;&lt;br /&gt;  [[args objectForKey:@"onFailure"] getValue:&amp;onFailure];&lt;br /&gt;   &lt;br /&gt;  NSError *error;&lt;br /&gt;  NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: &amp;error ];&lt;br /&gt;  NSString * responseString = [[NSString alloc] initWithData: returnData encoding: NSASCIIStringEncoding];&lt;br /&gt;   &lt;br /&gt;  if (!error) //connection succeeded; but did it work?&lt;br /&gt;   error = [Util getErrorFrom:responseString forRequest:request];&lt;br /&gt;   &lt;br /&gt;  if (error) {&lt;br /&gt;   NSLog(@"iTravelWrite Error ",error);&lt;br /&gt;   [caller performSelectorOnMainThread: onFailure withObject:error waitUntilDone:NO];&lt;br /&gt;  }&lt;br /&gt;  else&lt;br /&gt;   [caller performSelectorOnMainThread: onSuccess withObject:responseString waitUntilDone:NO];&lt;br /&gt;  [pool release];&lt;br /&gt; }&lt;br /&gt; return TRUE;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Pretty slick, huh?&lt;br /&gt;&lt;br /&gt;The three chief interface methods are, for a GET -&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;+(BOOL) doGet:(NSString *)urlString forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;and for a POST -&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;+(NSURLRequest*) buildRequestWithPostKeys:(NSArray *) postKeys postValues:(NSArray *) postValues urlString:(NSString *)urlString&lt;br /&gt;&lt;br /&gt;+(BOOL) doPost:(NSURLRequest *)request forCaller:(id)caller onSuccess:(SEL)onSuccess onFailure:(SEL)onFailure {&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;I'll give you an example of the former first, as it's easier. The call itself is perfectly straightforward:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; NSMutableString *urlString = [NSMutableString stringWithCapacity:128];&lt;br /&gt; [urlString appendString:[Util getSearchPageURL]];&lt;br /&gt; [urlString appendString:@"?locale="];&lt;br /&gt; [urlString appendString:[UserSettings getLanguage]];&lt;br /&gt; [urlString appendString:@"&amp;searchTerms="];&lt;br /&gt; [urlString appendString:[searchBar.text stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt; [HttpHelper doGet:urlString forCaller:self onSuccess:@selector(parseSearchResults:) onFailure:@selector(searchFailed:)];&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Not, however, the two "@selector" arguments. These must be methods on the caller, and they better expect an NSString* and an NSError*, respectively, as arguments. (Or they can take an NSObject* and cast from there, I suppose, but why bother, right?)&lt;br /&gt;&lt;br /&gt;Inside HttpHelper we have to wrap the selectors in NSValues to pass them from method to method, which is a bit annoying, but hey, we only have to do it once.&lt;br /&gt;&lt;br /&gt;To call a POST, by comparison:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; NSArray *postKeys = [NSArray arrayWithObjects:@"title", @"location", @"comments", @"pageUri", @"sectionName", @"sectionNumber", @"listingName", nil];&lt;br /&gt; NSArray *postValues = [NSArray arrayWithObjects:note.title, [note locationString], note.body, note.pageUri, note.sectionName, note.sectionNumber, note.listingName, nil];&lt;br /&gt; NSURLRequest *request = [HttpHelper buildRequestWithPostKeys:postKeys postValues:postValues urlString:[Util getUploadNoteURL]];&lt;br /&gt; [HttpHelper doPost:request forCaller:self onSuccess:@selector(onUploadSuccess) onFailure:@selector(onUploadError:)];&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Note that "onUploadSuccess" here doesn't take an argument - I don't care about the web site's response, the fact of success is all that matters. OK, dubious wisdom that, but the example is relevant to show that the selector methods don't actually have to accept arguments.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;We launch a new thread every time we call HttpHelper, so we have to be careful lest we run into concurrency problems. So we synchronize the actual HTTP calls in the "doHttp:" method, which is the one place in the app where we actually go out and connect to the big bad scary Internet.&lt;br /&gt;&lt;br /&gt;Note also that if we wanted to change from using "sendSynchronousRequest:" to the delegated version of NSURLConnection, the only class that would change is HttpHelper. Ah, encapsulation.&lt;br /&gt;&lt;br /&gt;Anyway. Share and enjoy, as the man said. Hope all that's useful to someone out there...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-7773801928876327755?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/7773801928876327755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/07/httphelper.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7773801928876327755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/7773801928876327755'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/07/httphelper.html' title='HttpHelper'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-583666261548870257</id><published>2009-07-06T19:19:00.000-07:00</published><updated>2009-07-06T20:18:55.342-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NSURLConnection'/><category scheme='http://www.blogger.com/atom/ns#' term='NSThread'/><category scheme='http://www.blogger.com/atom/ns#' term='UIBarButtonItem'/><category scheme='http://www.blogger.com/atom/ns#' term='performSelectorOnMainThread'/><category scheme='http://www.blogger.com/atom/ns#' term='customView'/><category scheme='http://www.blogger.com/atom/ns#' term='CLLocationManager'/><category scheme='http://www.blogger.com/atom/ns#' term='selector'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='UIActivityViewIndicator'/><category scheme='http://www.blogger.com/atom/ns#' term='SDK'/><category scheme='http://www.blogger.com/atom/ns#' term='threading'/><title type='text'>Step right up to view the fabulous ListingViewController!</title><content type='html'>Tickets &lt;strike&gt;$25&lt;/strike&gt; &lt;strike&gt;$5&lt;/strike&gt; &lt;strike&gt;$1&lt;/strike&gt; OK, fine, free.&lt;br /&gt;&lt;br /&gt;I have had a highly productive day with the iPhone SDK and now I am going to inflict it on you too. Specifically, I am going to show you the complete annotated code for the ListingViewController, the view controller which, despite its less than thrilling name, is kind of the heart of my iPhone application.&lt;br /&gt;&lt;br /&gt;Ready? Buckle your seat belts. It's gonna be a bumpy post.&lt;br /&gt;&lt;br /&gt;Here goes nothing: here's the header file -&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;//&lt;br /&gt;//  ListingsViewController.h&lt;br /&gt;//  iTravelWrite&lt;br /&gt;//&lt;br /&gt;//  Created by Jon Evans on 02/07/09.&lt;br /&gt;//&lt;br /&gt;&lt;br /&gt;#import &lt;UIKit/UIKit.h&gt;&lt;br /&gt;#import &lt;CoreLocation/CoreLocation.h&gt;&lt;br /&gt;#import "PageSearchResult.h"&lt;br /&gt;#import "ViewEntry.h"&lt;br /&gt;&lt;br /&gt;@interface ListingsViewController : UITableViewController &lt;CLLocationManagerDelegate&gt; {&lt;br /&gt; NSManagedObjectContext *managedObjectContext;     &lt;br /&gt;        CLLocationManager *locationManager;&lt;br /&gt; PageSearchResult *page;&lt;br /&gt; ViewEntry *rootEntry;&lt;br /&gt;        UIBarButtonItem *addNoteButton;&lt;br /&gt; UIBarButtonItem *editListingButton;&lt;br /&gt; UIBarButtonItem *iAmThereButton;&lt;br /&gt; BOOL createdRoot;&lt;br /&gt;}&lt;br /&gt;-(void)addNote;&lt;br /&gt;-(void)editListing;&lt;br /&gt;-(void)iAmThere;&lt;br /&gt;&lt;br /&gt;@property (nonatomic, retain) NSManagedObjectContext *managedObjectContext;&lt;br /&gt;@property (nonatomic, retain) CLLocationManager *locationManager;&lt;br /&gt;@property (nonatomic, retain) PageSearchResult *page;&lt;br /&gt;@property (nonatomic, retain) ViewEntry *rootEntry;&lt;br /&gt;@property (nonatomic, retain) UIBarButtonItem *addNoteButton;&lt;br /&gt;@property (nonatomic, retain) UIBarButtonItem *editListingButton;&lt;br /&gt;@property (nonatomic, retain) UIBarButtonItem *iAmThereButton;&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Lots of stuff there, most of it self-explanatory. A few bits you won't recognize:&lt;br /&gt;&lt;br /&gt;"PageSearchResult" comes from the previous view in the hierarchy, in which the user searches WikiTravel (or selects from previously used pages), and contains the name and URL of the page that the user wishes to edit.&lt;br /&gt;&lt;br /&gt;"ViewEntry" is a horrifically generic name and I should probably rename it to "WikiTravelEntry" or something like it. See, WikiTravel pages have intrinsic hierarchies. The "Toronto" page, for instance, has sections like "Districts" and "Eat", which in turn have subsections like "Islands" and "Farmer's Markets". Meanwhile, individual listings - like "Royal Ontario Museum" and "Yumi Japanese Restaurant" - can be attached to any section at all.&lt;br /&gt;&lt;br /&gt;Too complicated? Let me give you an analogy. You've got your iPod, right? Well, under the "Music" heading, you can have a list of Artists; each Artist can have a list of Albums; and individual Songs can appear under Music, Artist, or Album.&lt;br /&gt;&lt;br /&gt;Think of a WikiTravel listing as a Song, and a WikiTravel section as a folder. Well, a ViewEntry can represent either a Section or an individual Listing; and if it's a Section, then it can have children, and they too can be Sections or Listings.&lt;br /&gt;&lt;br /&gt;Huh. So WikiTravel is basically laid out in the same way as an iPod's music. Hey, does that mean the iPhone SDK, with its seamless hierarchy-navigation UI tools, is totally perfect for a WikiTravel iPhone app?&lt;br /&gt;&lt;br /&gt;Dude. Does it ever. Check it out:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;//&lt;br /&gt;//  ListingsViewController.m&lt;br /&gt;//  iTravelWrite&lt;br /&gt;//&lt;br /&gt;//  Created by Jon Evans on 02/07/09.&lt;br /&gt;//&lt;br /&gt;&lt;br /&gt;#import "ListingsViewController.h"&lt;br /&gt;#import "NoteEditController.h"&lt;br /&gt;#import "ListingEditController.h"&lt;br /&gt;#import "HttpHelper.h"&lt;br /&gt;#import "Util.h"&lt;br /&gt;#import "Note.h"&lt;br /&gt;&lt;br /&gt;@implementation ListingsViewController&lt;br /&gt;&lt;br /&gt;@synthesize managedObjectContext, locationManager, page, rootEntry, addNoteButton, editListingButton, iAmThereButton;&lt;br /&gt;&lt;br /&gt;- (void)viewDidLoad {&lt;br /&gt;    [super viewDidLoad];&lt;br /&gt; &lt;br /&gt; //nav bar&lt;br /&gt; //TODO: localize this string, and those below&lt;br /&gt; self.navigationItem.title=@"Loading...";&lt;br /&gt; &lt;br /&gt; //location manager&lt;br /&gt; [[self locationManager] startUpdatingLocation];&lt;br /&gt;&lt;br /&gt; //toolbar&lt;br /&gt; self.iAmThereButton = [[UIBarButtonItem alloc] initWithTitle:@"I'm There!" style:UIBarButtonItemStyleBordered&lt;br /&gt;                   target:self action:@selector(iAmThere)]; &lt;br /&gt; iAmThereButton.enabled=NO;&lt;br /&gt; self.addNoteButton = [[UIBarButtonItem alloc] initWithTitle:@"Note To Self" style:UIBarButtonItemStyleBordered&lt;br /&gt;                   target:self action:@selector(addNote)];&lt;br /&gt; addNoteButton.enabled=NO;&lt;br /&gt; self.editListingButton = [[UIBarButtonItem alloc] initWithTitle:@"Edit Listing" style:UIBarButtonItemStyleBordered&lt;br /&gt;                  target:self action:@selector(editListing)]; &lt;br /&gt; editListingButton.enabled=NO;&lt;br /&gt;&lt;br /&gt; self.toolbarItems = [NSArray arrayWithObjects:iAmThereButton, addNoteButton, editListingButton, nil];&lt;br /&gt; [self.navigationController setToolbarHidden:NO];&lt;br /&gt;&lt;br /&gt; //activity button&lt;br /&gt; UIActivityIndicatorView *loading = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite];&lt;br /&gt; loading.frame=CGRectMake(0.0, 0.0, 25.0, 25.0);&lt;br /&gt; [loading sizeToFit];&lt;br /&gt; loading.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);&lt;br /&gt; UIBarButtonItem *activityButton = [[UIBarButtonItem alloc] initWithCustomView:loading];&lt;br /&gt; self.navigationItem.rightBarButtonItem = activityButton;&lt;br /&gt; [loading release];&lt;br /&gt; [activityButton release];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;This is pretty basic stuff: assign text and buttons to the navigation bar and toolbar you get for free by subclassing UITableViewController.&lt;br /&gt;&lt;br /&gt;Perhaps most interesting is the bit at the end, where I assign a UIActivityIndicatorView - that little swirly "something is happening" animated wheel - to a custom UIBarButton. Such a beast is invisible until you tell it "startAnimating". Which we do next, which is when things get interesting -&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;//Fill the table&lt;br /&gt;- (void)viewDidAppear:(BOOL)animated&lt;br /&gt;{&lt;br /&gt; UIActivityIndicatorView *loading = (UIActivityIndicatorView*) self.navigationItem.rightBarButtonItem.customView;&lt;br /&gt; if (!rootEntry) { //only load for the top of the hierarchy&lt;br /&gt;  [loading startAnimating];&lt;br /&gt;  NSThread* loadThread = [[NSThread alloc] initWithTarget:self selector:@selector(loadViewEntries) object:nil];&lt;br /&gt;  [loadThread start];&lt;br /&gt;  [loadThread release];&lt;br /&gt;  createdRoot = YES;&lt;br /&gt; }&lt;br /&gt; else&lt;br /&gt; {&lt;br /&gt;  createdRoot = NO;&lt;br /&gt;  self.navigationItem.title=rootEntry.sectionName;&lt;br /&gt;  [self.tableView reloadData];&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;So. First, you may be wondering, "why is this in viewDidAppear rather than viewDidLoad?" The main reason is that it can take a while to load the data, and I don't want the app to freeze up completely for a few seconds.&lt;br /&gt;&lt;br /&gt;Now, if "rootEntry" is nil, that means we have to go out to the Internet and get our data. But wait! If we were to go load this view's contents from the Internet in the same thread, the UI would block. If we want to feign responsiveness, and reassure the user that something is indeed happening with the UIActivityIndicatorView swirly, then we have to launch a new background thread. Which we do.&lt;br /&gt;&lt;br /&gt;The whole "createdRoot" thing is really kind of ugly, but necessary to avoid deallocation disaster. If it's any kind of consolation, it's only ever used in dealloc.&lt;br /&gt;&lt;br /&gt;So. Our new thread goes and calls the "loadViewEntries" selector. Note that we didn't even define "loadViewEntries" in the header file, but selectors work at run-time, so it doesn't matter. It's almost like the Smalltalk "perform:" notation. Which is kinda awesome. Oh, Objective-C, I love you, when I don't hate you.&lt;br /&gt;&lt;br /&gt;So our thread reaches out and calls:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void)loadViewEntries&lt;br /&gt;{&lt;br /&gt; //autorelease pool&lt;br /&gt; NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];&lt;br /&gt; &lt;br /&gt; //load listings&lt;br /&gt; NSString *urlRoot = [[Util getListingsURL] stringByAppendingString:@"?page="];&lt;br /&gt; NSString *urlString = [urlRoot stringByAppendingString:[page.urlSuffix stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];&lt;br /&gt; NSURL *url = [NSURL URLWithString:urlString];&lt;br /&gt; NSURLRequest *request = [NSURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:10.0];&lt;br /&gt; NSData *returnData = [NSURLConnection sendSynchronousRequest: request returningResponse: nil error: nil ];&lt;br /&gt; NSString * responseString = [[NSString alloc] initWithData: returnData encoding: NSASCIIStringEncoding];&lt;br /&gt; // [Util doAlert:@"Response" withMessage:responseString];&lt;br /&gt; &lt;br /&gt; //parse listings&lt;br /&gt; self.rootEntry = [[ViewEntry alloc] initWithValueString:responseString levelsFromTop:0];&lt;br /&gt; rootEntry.sectionName=page.pageName;&lt;br /&gt; [self performSelectorOnMainThread: @selector(loadFinished) withObject:nil waitUntilDone:NO];&lt;br /&gt; [pool release];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Two key things to note here. One is that we create an autorelease pool at the beginning of this method, and release it at the end. You have to do this whenever you create a new thread, or you face major memory leakage. What a hassle, and what a disaster if you forget. Oh, Objective-C, I hate you when I don't love you.&lt;br /&gt;&lt;br /&gt;Next - and this is important - you &lt;i&gt;cannot call UIKit&lt;/i&gt;, which in this context basically means anything to do with any view, from inside your new spawned thread. If you do, your app will die with "Tried to obtain the web lock with a thread other than the main thread or the web thread. This may be a result of calling to UIKit from a secondary thread." Oh, Objective-C, I hate you etc.&lt;br /&gt;&lt;br /&gt;(Also: &lt;i&gt;web thread&lt;/i&gt;? Where can I get me a piece of that?)&lt;br /&gt;&lt;br /&gt;At least they give you a fairly easy way around it: the "performSelectorOnMainThread: withObject: waitUntilDone:" method on NSObject. Which works just fine, but means you have to create yet another method to do whatever you want done when the thread completes:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void)loadFinished&lt;br /&gt;{&lt;br /&gt; UIActivityIndicatorView *loading = (UIActivityIndicatorView*) self.navigationItem.rightBarButtonItem.customView;&lt;br /&gt; self.navigationItem.title=rootEntry.sectionName;&lt;br /&gt; [loading stopAnimating];&lt;br /&gt; [self.tableView reloadData];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;OK. So we've populated our hierarchy of data (which happens offscreen in ViewEntry's "init" method.) Now what are we going to do with it?&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark TableView&lt;br /&gt;&lt;br /&gt;- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {&lt;br /&gt;    return [[rootEntry getChildren] count];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {&lt;br /&gt; &lt;br /&gt; static NSString *CellIdentifier = @"ViewEntryCell";&lt;br /&gt; &lt;br /&gt; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];&lt;br /&gt; if (cell == nil)&lt;br /&gt;  cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];&lt;br /&gt; &lt;br /&gt; // Set up the cell...&lt;br /&gt; ViewEntry *entry = [[rootEntry getChildren] objectAtIndex:indexPath.row]; &lt;br /&gt; cell.textLabel.text = [entry getName];&lt;br /&gt; if ([entry hasChildren])&lt;br /&gt;  cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;&lt;br /&gt; else&lt;br /&gt;  cell.accessoryType=UITableViewCellAccessoryNone;&lt;br /&gt; &lt;br /&gt; return cell;&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;And that's all you need to do to show the data to the user. Pretty slick, eh? Did I mention that this entire view is created programmatically, with no nib file? Did you notice I never actually created or laid out a view? If your app fits into a hierarchy of TableViews, and fortunately mine fits &lt;i&gt;perfectly&lt;/i&gt;, the iPhone SDK is really a dream to work with.&lt;br /&gt;&lt;br /&gt;So we show the user the current section's children, which can be subsections or listings (think albums or songs). If the former, we show a little chevron which means you can drill down the hierarchy further. And when the user selects...&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {&lt;br /&gt; ViewEntry *selectedEntry = [[rootEntry getChildren] objectAtIndex:indexPath.row];&lt;br /&gt; if ([selectedEntry hasChildren]) {&lt;br /&gt;  //push this subsection onto the stack&lt;br /&gt;  ListingsViewController *lvController = [[ListingsViewController alloc] initWithNibName:nil bundle:nil];&lt;br /&gt;  lvController.managedObjectContext = self.managedObjectContext;&lt;br /&gt;  lvController.locationManager = self.locationManager;&lt;br /&gt;  lvController.page = self.page;&lt;br /&gt;  lvController.rootEntry = selectedEntry;&lt;br /&gt;  [self.navigationController pushViewController:lvController animated:YES];&lt;br /&gt;  [lvController release];&lt;br /&gt; }&lt;br /&gt; else {&lt;br /&gt;  addNoteButton.enabled=YES;&lt;br /&gt;  editListingButton.enabled=YES;&lt;br /&gt;  if ([locationManager location])&lt;br /&gt;   iAmThereButton.enabled=YES;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;...we either create a new instance of this very same controller, pass the relevant data to it, and push it onto the view stack; or, if it's a listing (eg "Royal Ontario Museum"), we enable the three do-something-with-a-listing buttons in the toolbar.&lt;br /&gt;&lt;br /&gt;Yes, that's right, that's &lt;i&gt;all the code we need&lt;/i&gt; to navigate an arbitrarily sized view hierarchy by creating a tree of ListingViewControllers on the fly and stacking them atop one another. The SDK handles navigation and so forth for us. Cool, eh?&lt;br /&gt;&lt;br /&gt;What do those buttons do? Well...&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Business logic&lt;br /&gt;&lt;br /&gt;-(void) addNote {&lt;br /&gt; NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];&lt;br /&gt; ViewEntry *listing = [[rootEntry getChildren] objectAtIndex:indexPath.row];&lt;br /&gt; Note *newNote = (Note *)[NSEntityDescription insertNewObjectForEntityForName:@"Note" inManagedObjectContext:managedObjectContext];&lt;br /&gt; &lt;br /&gt; // Configure the new note&lt;br /&gt; [newNote setIsUploaded:[NSNumber numberWithBool:FALSE]];&lt;br /&gt; [newNote setCreationDate:[NSDate date]];&lt;br /&gt; [newNote setUserEmail:[Util getEmail]];&lt;br /&gt; [newNote setSectionName:[listing sectionName]];&lt;br /&gt; [newNote setSectionNumber:[listing sectionNumber]];&lt;br /&gt; [newNote setListingName:[listing listingName]];&lt;br /&gt; [newNote setPageUri:[page urlSuffix]];&lt;br /&gt; CLLocation *location = [locationManager location];&lt;br /&gt; if (location) {&lt;br /&gt;  CLLocationCoordinate2D coordinate = [location coordinate];&lt;br /&gt;  [newNote setLatitude:[NSNumber numberWithDouble:coordinate.latitude]];&lt;br /&gt;  [newNote setLongitude:[NSNumber numberWithDouble:coordinate.longitude]];&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; NoteEditController *noteEditController = [NoteEditController alloc];&lt;br /&gt; noteEditController.note = newNote;&lt;br /&gt; [self.navigationController pushViewController:noteEditController animated:YES];&lt;br /&gt; [noteEditController release];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Here you get to see a) part of the Core Data persistence layer in action, b) the LocationManager where-am-I code in action, and c) the creation of a new kind of detail-view controller.&lt;br /&gt;&lt;br /&gt;a) We get the ManagedObjectContext from our parent ViewController, who in turn got it from the RootViewController, who in turn got it from the AppDelegate. There are times - such as when you want to save one object, but not another - when you might want more than one ManagedObjectContext, but I haven't bumped into them yet. I think I might be able to get it from the AppDelegate which serves as something of a global, but for now I'm passing them from ViewController to ViewController.&lt;br /&gt;&lt;br /&gt;And yeah, "Note" is a horribly generic name. I oughta rename it to "TravelNote" or something. The idea is that, rather than deal with the detailed listing-edit screen, you can jot a few words where you are, and upload that Note to wetravelwrite.com. Then, later,you'll get an email reminder (if you've opted for them) complete with a link, so that when you're at a computer with a real keyboard and screen, you can go online and edit that lasting as per your note and location.&lt;br /&gt;&lt;br /&gt;b) We construct the LocationManager in a method further below.&lt;br /&gt;&lt;br /&gt;c) OK, so it's not that exciting.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void)editListing{&lt;br /&gt; NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];&lt;br /&gt; ViewEntry *listing = [[rootEntry getChildren] objectAtIndex:indexPath.row];&lt;br /&gt;&lt;br /&gt; ListingEditController *listingEditController = [ListingEditController alloc];&lt;br /&gt; listingEditController.listingData = [listing listingData];&lt;br /&gt; NSArray *keys = [NSArray arrayWithObjects:@"oldName", @"sectionToEdit", @"sectionNumber", @"editPageUrl", nil];&lt;br /&gt; NSArray *values = [NSArray arrayWithObjects:[listing listingName], [listing sectionName], [listing sectionNumber],  [page urlSuffix], nil];&lt;br /&gt; NSDictionary *listingMetaData = [NSDictionary dictionaryWithObjects:values forKeys: keys];&lt;br /&gt; listingEditController.listingMetadata = listingMetaData;&lt;br /&gt;&lt;br /&gt; [self.navigationController pushViewController:listingEditController animated:YES];&lt;br /&gt; [listingEditController release];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Pretty much the same thing, only messier. That's a lot of crap passing between ViewControllers, and I'd be tempted to abstract it into an object if this happened anywhere else; fortunately, it doesn't. And the crap is necessary, alas. The detail controller includes seventeen fields. Yes, &lt;i&gt;seventeen&lt;/i&gt;. Which is probably OK if you're just tweaking a few details of an existing listing, but is also the reason I added the note-to-future-self functionality. I am going to add an "Add New Listing" button, but I don't expect too many people to use it.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;-(void)iAmThere{&lt;br /&gt; NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];&lt;br /&gt; ViewEntry *listing = [[rootEntry getChildren] objectAtIndex:indexPath.row];&lt;br /&gt;&lt;br /&gt; //construct location string&lt;br /&gt; CLLocation *location = [locationManager location];&lt;br /&gt; CLLocationCoordinate2D coordinate = [location coordinate];&lt;br /&gt; NSString *locationString = [[NSNumber numberWithDouble:coordinate.latitude] stringValue];&lt;br /&gt; locationString = [locationString stringByAppendingString:@","];&lt;br /&gt; locationString = [locationString stringByAppendingString:[[NSNumber numberWithDouble:coordinate.longitude] stringValue]];&lt;br /&gt;&lt;br /&gt; //build http request&lt;br /&gt; NSArray *postKeys = [NSArray arrayWithObjects:@"pageUri", @"sectionName", @"sectionNumber", @"listingName", @"location", nil];&lt;br /&gt; NSArray *postValues = [NSArray arrayWithObjects:[page urlSuffix], listing.sectionName, listing.sectionNumber, listing.listingName, locationString, nil];&lt;br /&gt; HttpHelper *http = [[HttpHelper alloc] initWithPostKeys:postKeys postValues:postValues urlString:[Util getLocationUpdateURL]];&lt;br /&gt; NSURLRequest *request = [http request];&lt;br /&gt;&lt;br /&gt; //create connection&lt;br /&gt; NSURLConnection *theConnection = [NSURLConnection connectionWithRequest:request delegate: self];&lt;br /&gt; if (!theConnection) {&lt;br /&gt;  // inform the user that the download could not be performed&lt;br /&gt;  // TODO: real error handling&lt;br /&gt;  [Util doAlert:@"Connection creation failure" withMessage:@"Connection failed"];&lt;br /&gt; }&lt;br /&gt; [http release];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)connection:(NSURLConnection *)connection&lt;br /&gt;  didFailWithError:(NSError *)error&lt;br /&gt;{&lt;br /&gt;    // release the connection, and inform the user&lt;br /&gt; [Util handleError:@"Connection Failure" withError: error];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)connectionDidFinishLoading:(NSURLConnection *)connection&lt;br /&gt;{&lt;br /&gt; //TODO: add checkbox indicating listing located&lt;br /&gt; [Util doAlert:@"Success" withMessage:@"Connection finished"];&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Here we have an HTTP connection. The idea being that people can geotag WikiTravel listings with one button; you're passing by the Royal Ontario Museum, you check your iPhone app, you notice that it does not yet have a geotag (I haven't yet, but will, added checkboxes to indicate this), and you push the "I'm there!" button to update its WikiTravel listing with your current location, no typing necessary.&lt;br /&gt;&lt;br /&gt;Now, you can perform an HTTP request in a single line, with NSURLConnection's sendSynchronousRequest: method, but this one uses delegation, so we implement the "connectionDidFail..." and "connectionDidFinish" methods.&lt;br /&gt;&lt;br /&gt;HttpHelper is my own class, used to give me a central point of control over all HttpRequests, partly so I didn't have to keep copy-pasting code, partly so I can handle caching in one place. (Although I think I may have to add a new "keep or clear cache" to its init.) Util is my own class that holds basic fixed values and utility methods.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Location manager&lt;br /&gt;/**&lt;br /&gt; Return a location manager -- create one if necessary.&lt;br /&gt; */&lt;br /&gt;- (CLLocationManager *)locationManager {&lt;br /&gt; &lt;br /&gt;    if (locationManager != nil) {&lt;br /&gt;  return locationManager;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; self.locationManager = [[CLLocationManager alloc] init];&lt;br /&gt; [locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];&lt;br /&gt; [locationManager setDelegate:self];&lt;br /&gt; &lt;br /&gt; return locationManager;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; Conditionally enable the Add button:&lt;br /&gt; If the location manager is generating updates, then enable the button;&lt;br /&gt; If the location manager is failing, then disable the button.&lt;br /&gt; */&lt;br /&gt;- (void)locationManager:(CLLocationManager *)manager&lt;br /&gt;    didUpdateToLocation:(CLLocation *)newLocation&lt;br /&gt;           fromLocation:(CLLocation *)oldLocation {&lt;br /&gt; if ([[self tableView] indexPathForSelectedRow]) {&lt;br /&gt;  iAmThereButton.enabled = YES;&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;- (void)locationManager:(CLLocationManager *)manager&lt;br /&gt;       didFailWithError:(NSError *)error {&lt;br /&gt;    iAmThereButton.enabled = NO;&lt;br /&gt;}&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;And that's how you use the location manager; like much of the iPhone SDK, it's a delegate pattern. You create it and set its delegate; it goes out and tries to work out where you are; when it succeeds or fails, it lets you know. Simple, really.&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#pragma mark -&lt;br /&gt;#pragma mark Cleanup&lt;br /&gt;&lt;br /&gt;- (void)dealloc {&lt;br /&gt; [addNoteButton release];&lt;br /&gt; [editListingButton release];&lt;br /&gt; [iAmThereButton release];&lt;br /&gt; if (createdRoot) {&lt;br /&gt;  [rootEntry release];&lt;br /&gt;  [locationManager release];&lt;br /&gt; }&lt;br /&gt;    [super dealloc];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;@end&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Note that we only release the rootEntry and locationManager if we're the king ViewController at the top of the hierarchy. Otherwise we're asking for serious EXC_BAD_ACCESS disaster.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Aaaaaand there you go. The whole ListingViewController, in all its glory.&lt;br /&gt;&lt;br /&gt;Note, he mentioned with some pride, that I started it writing it only four days ago - and that it's only maybe a quarter of the code I've written and tested since then.&lt;br /&gt;&lt;br /&gt;One last caveat. You'll notice there's no copyright claim up above, and you're welcome to use the code as is, but - while it's pretty much running now, there are still tweaks I want to make. Error handling and localization, in particular. And I have hardly any unit tests yet. So if your need isn't dire and urgent, I advise you to wait until I officially release the code at code.google.com, under an open-source license, and post to this blog to that effect.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-583666261548870257?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/583666261548870257/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/07/step-right-up-to-view-fabulous.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/583666261548870257'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/583666261548870257'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/07/step-right-up-to-view-fabulous.html' title='Step right up to view the fabulous ListingViewController!'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-2638379771580714879</id><published>2009-07-02T19:47:00.000-07:00</published><updated>2009-07-05T09:29:38.768-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='Objective-C'/><category scheme='http://www.blogger.com/atom/ns#' term='EXC_BAD_ACCESS'/><category scheme='http://www.blogger.com/atom/ns#' term='SDK'/><category scheme='http://www.blogger.com/atom/ns#' term='doesNotRecognizeSelector'/><category scheme='http://www.blogger.com/atom/ns#' term='Apple'/><title type='text'>Don't call me an Objectivist...</title><content type='html'>...but I speak a new language now.&lt;br /&gt;&lt;br /&gt;Well, sorta. I write to you on my shiny new MacBook, which I purchased in part because you can't write iPhone applications on non-Macs. I guess I don't resent that. Much. It is a nice machine, although I'm still getting used to all of OS X's quirks.&lt;br /&gt;&lt;br /&gt;But never mind the OS: writing an iPhone version of my app means mastering the iPhone SDK and a whole new language, Objective-C. Which was kind of frustrating for a week. Imagine, for a moment, that you're a car mechanic. Now imagine that you move to a new garage, where the cars are totally different, not just the engines but even the doors and the dashboard controls are laid out in some bizarre new arrangement, and instead of your old tools, there's a wall full of all these curvy New Age things that look more like vacuum cleaner parts and art objects than hex wrenches and hammers. That's kind of what diving into the iPhone SDK was like.&lt;br /&gt;&lt;br /&gt;But. You are a car mechanic. And ultimately, we're still talking about wheeled vehicles powered by internal combustion engines; so after a bewildering week, during which you often don't even know what questions to ask, you begin to realize that in fact these cars are quite elegantly if counterintuitively designed, and that these tools are well suited to them.&lt;br /&gt;&lt;br /&gt;Yeah, so the metaphor got out of hand, sue me. Anyway, I didn't know Objective-C, but I've been writing software professionally for nigh on twenty years now, and it's kind of a weird mixture of C and Smalltalk, both of which I have worked with before. It turns out that it is a powerful and flexible language, and the iPhone SDK's tools and libraries do indeed ultimately make sense.&lt;br /&gt;&lt;br /&gt;Don't get me wrong. There are still things I hate. Mostly the hangovers from C. Just like C, object you create requires two files: one where all the code goes, and a "header file" that describes the code. It was a bad idea then, and it's a bad idea now, which is why no modern languages require it. The resulting profusion of files is messy, annoying, completely unnecessary, and provides more places for things to go wrong.&lt;br /&gt;&lt;br /&gt;Also, you have to allocate memory by hand, and then be extremely careful to de-allocate it, lest you leak memory and consume all your phone's resources. I shake my head with bewildered contempt. Manual memory management is archaic, clumsy, tedious, and perpetually prone to disaster. Making it part of the iPhone SDK is like having a beautifully renovated marble-and-gold bathroom built atop lead plumbing.&lt;br /&gt;&lt;br /&gt;(My understanding is that if you're writing for Macs, you can have automatic garbage collection, like the rest of the civilized world, but such is strongly discouraged on the iPhone. Scarce resources, apparently. But I note that Java development for similarly specced Android phones, with garbage collection - and sans useless header files - works just fine...)&lt;br /&gt;&lt;br /&gt;I have also learned that there are subtle pitfalls in the Objective-C environment. I'll concede that this can be true of Java as well, though usually that's when you're dealing with classpaths, less of an issue when doing Android development. But, well, see for yourself. Here's what looks like a garden-variety call to a Settings class:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; NSMutableString *urlString = [NSMutableString stringWithCapacity:128];&lt;br /&gt; [urlString appendString:[Settings getSearchPageURL]];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;which should call the "getSearchPageURL" class method on my "Settings" class, which right now is pretty much just a bundle of constants. Here's a simplified Settings.h:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#import &amp;lt;UIKit/UIKit.h&amp;gt;&lt;br /&gt;&lt;br /&gt;@interface Settings {&lt;br /&gt;}&lt;br /&gt;+(NSString *) getSearchPageURL;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and Settings.m:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;+(NSString *)getSearchPageURL&lt;br /&gt;{&lt;br /&gt; return @"http://wetravelwrite.appspot.com/mb/searchForPage";&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Nothing to it, right? Compiled just fine. Did it run? Did it hell. Instead I got &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;2009-07-02 22:48:28.519 iTravelWrite[17002:20b] *** NSInvocation: warning: object 0xaa40 of class 'Settings' does not implement methodSignatureForSelector: -- trouble ahead&lt;br /&gt;2009-07-02 22:48:28.520 iTravelWrite[17002:20b] *** NSInvocation: warning: object 0xaa40 of class 'Settings' does not implement doesNotRecognizeSelector: -- abort&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OK, points for the amusing "trouble ahead" warning, but what the hell? I'm just calling a very simple function that does nothing but return a hardcoded string - so what could possibly have gone wrong?&lt;br /&gt;&lt;br /&gt;Turns out that function automatically gets abstracted up at runtime into a selector - and for that to work, Settings must extend NSObject. Which is trivially done, of course, but far from intuitive.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Wacko problem number 2: I changed a variable from an NSArray to an NSDictionary, and reworked the code appropriately - no big deal - and suddenly the app was dying with an EXC_BAD_ACCESS call, meaning that I was trying to release the memory of an object I had already released. Muttering foul imprecations about the whole notion of manual garbage collection, I went carefully through my code and found ... nothing. So I Googled. (Which is how I ultimately solved the previous problem, too; but in both cases I had to go well past the first page of results, which is one reason I'm writing this with copious details. Maybe future sufferers will find their way to this post.)&lt;br /&gt;&lt;br /&gt;Here are the relevant parts of the header file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@interface SearchController : UITableViewController &lt;UISearchBarDelegate&gt; {&lt;br /&gt; NSDictionary *searchResults;&lt;br /&gt;}&lt;br /&gt;@property (nonatomic, retain) NSDictionary *searchResults;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;and it turns out that the problematic line in the implementation was this one:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;searchResults = [NSDictionary dictionaryWithObjects: urlSuffixes forKeys: pageNames];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Looks harmless enough, doesn't it? Both urlSuffixes and pageNames are perfectly acceptable NSArrays of NSStrings. So what's the problem?&lt;br /&gt;&lt;br /&gt;Well, if you're not familiar with Objective-C, you might be a little gobsmacked to learn that the solution was to change the above line to&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;self.searchResults = [NSDictionary dictionaryWithObjects: urlSuffixes forKeys: pageNames];&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Yes, really.&lt;br /&gt;&lt;br /&gt;As far as I can tell, the difference is that in the second example, instead of the instance variable being directly modified, the @synthesized auto-generated setter is called, with "retain" as dictated in the @property definition, which prevents the NSDictionary from being released when we exit the method which generates it. Perhaps some people think of this stuff as intuitive. Not me. It's all fixed and working now, but this kind of man-behind-the-curtain stuff makes me a little uneasy.&lt;br /&gt;&lt;br /&gt;That said, there's a lot to like. The user-interface stuff, once you figure it out, is (mostly) a joy to work with. You can lay out items visually or programmatically; you get a built-in navigation header and button toolbar, yours for just a few lines of code; it's easy to reach out via HTTP, with a one-line synchronous-call version, a delegated version with fine control, or a compromise somewhere between; and as of the 3.0 SDK, instead of hand-writing SQL code to access SQLite, you can use Apple's Core Data engine to manage all your persistent data. (Which comes with its own problems but overall is definitely more elegant and more powerful.)&lt;br /&gt;&lt;br /&gt;Also, while I wouldn't say that I'm fluent in Objective-C yet, I'm now conversant, and I like it a lot when it's reminding me of Smalltalk, and not reminding me of C. You do, however, seem to wind up writing a lot more lines of code than you would in Java to do the same thing. It's like speaking German vs. English.&lt;br /&gt;&lt;br /&gt;Overall, though, qualified thumbs up. I still prefer Java to Objective-C, and there are still things that Android can do and the iPhone can't - multitasking is the biggest, and the associated inter-app communication possibilities - while I can't think of any examples of the converse. But I am increasingly a fan of the latter's SDK. What at first looked a bit like a junkyard is now becoming more of a playground.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-2638379771580714879?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/2638379771580714879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/07/dont-call-me-objectivist.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/2638379771580714879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/2638379771580714879'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/07/dont-call-me-objectivist.html' title='Don&apos;t call me an Objectivist...'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-3308177959918236566</id><published>2009-06-17T20:50:00.000-07:00</published><updated>2009-09-06T08:22:43.335-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='HTTP'/><category scheme='http://www.blogger.com/atom/ns#' term='POST'/><category scheme='http://www.blogger.com/atom/ns#' term='GET'/><category scheme='http://www.blogger.com/atom/ns#' term='Android'/><title type='text'>I am the alpha dog!</title><content type='html'>By which I mean, my Android app is now in alpha test: I just uploaded it onto my HTC Magic, and whaddaya know, it runs, it doesn't crash, it actually does stuff. Indeed, I just updated WikiTravel via its interface. (Slightly buggily, but hey, alpha test.)&lt;br /&gt;&lt;br /&gt;Mind you, the Android app is a fairly trivial piece of programming next to the AppEngine middleware that does the actual parsing and updating of WikiTravel. Which is the way it should be. Resources are scarce on a smartphone, and both processing and battery power are relatively meagre; I expect the design pattern of choice is going to be "get as much of the work done as you can on your server farm, then communicate simple low-bandwidth stuff to the phone."&lt;br /&gt;&lt;br /&gt;Ultimately, the phone isn't much more than a user interface. In fact, now that I think about it, my whole app follows the classic Model-View-Controller design: WikiTravel is the model, the smartphone is the view, and my AppEngine service is the controller. Huh. Plus ça change, plus c'est la même chose.&lt;br /&gt;&lt;br /&gt;Anyway, a list of tips, tricks, and annoyances since last we met:&lt;br /&gt;&lt;UL&gt;&lt;br /&gt;&lt;LI&gt;Let's start off with the annoyances. Character encoding. This has never been my strong suit, and I all too often wind up trial-and-erroring a solution. What doesn't help here is that I've got URL encoding (eg "?"-&gt;"%3F" in an HTTP request), HTML encoding (eg &amp; -&gt; &amp;amp;amp; in a web page) and actual character encoding (ASCII? Unicode? UTF-8?) and while I grok the first two, I always have a mental block with the latter. I seem to have brute-forced something like a solution. We'll see. I expect it will rear its ugly head again.&lt;P&gt;&lt;br /&gt;Python, weirdly, for all its text-processing power, doesn't come with much in the way of built-in HTML escaping and unescaping. "cgi.escape" will do much, but, annoyingly, not everything in the way of escaping. As for unescaping, fuhgedaboudit. Fortunately, I found this neat little solution somewhere on the Internets:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;#HTML unescape&lt;br /&gt;#taken from the Internet&lt;br /&gt;class Unescaper:&lt;br /&gt;    entity_re = re.compile(r'&amp;(#?[A-Za-z0-9]+?);')&lt;br /&gt;&lt;br /&gt;    def replace_entities(self,match):&lt;br /&gt;        try:&lt;br /&gt;            ent = match.group(1)&lt;br /&gt;            if ent[0] == "#":&lt;br /&gt;                if ent[1] == 'x' or ent[1] == 'X':&lt;br /&gt;                    return unichr(int(ent[2:], 16))&lt;br /&gt;                else:&lt;br /&gt;                    return unichr(int(ent[1:], 10))&lt;br /&gt;            return unichr(name2codepoint[ent])&lt;br /&gt;        except:&lt;br /&gt;            return match.group()&lt;br /&gt;&lt;br /&gt;    def html_unescape(self,data):&lt;br /&gt;        return self.entity_re.sub(self.replace_entities, data)&lt;br /&gt;&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;LI&gt;While I'm complaining, as a Java coder, I'm used to all objects having a "toString" representation, and thus, when logging or debugging, being able to put &lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;"this is"+anObject+" and this is "+anOther&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;without worrying about syntax. But Python, otherwise a far superior text-processing lanaguage, won't let you do this: you have to put "str()" or "repr()" around objects you want to include in a string. For no apparent reason. Sigh.&lt;br /&gt;&lt;LI&gt;The good news is, the Java SDK now includes a perfectly acceptable HTML parser. I needed to parse attributes in a tag, didn't want to include a whole new JAR in my project for just that purpose, and really didn't want to write that code myself (it'd be like reinventing the wheel in Detroit.) org.xml.sax to the rescue:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt; class ListingHandler extends DefaultHandler {&lt;br /&gt;  public void startElement(String namespaceUri, String localName, String qualifiedName,&lt;br /&gt;                Attributes attributes) throws SAXException {&lt;br /&gt;      for (int i=0; i&amp;lt;attributes.getLength(); i++) {&lt;br /&gt;       String field = attributes.getLocalName(i);&lt;br /&gt;       String value = attributes.getValue(i);&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;and you don't even have to fuss with XML namespaces if you don't want to (and here, I don't.)&lt;br /&gt;&lt;LI&gt;GAEUnit continues to be awesome. Android unit testing continues to be much clumsier. Yet another reason for AppEngine to do most of the tricky work.&lt;br /&gt;&lt;LI&gt;Optimization. Always a thorny issue. As a wise programmer once taught me:&lt;b&gt;&lt;br /&gt;&lt;UL&gt;&lt;LI&gt;The first rule of optimization: don't do it.&lt;br /&gt;&lt;LI&gt;The second rule of optimization (For Experts Only): don't do it &lt;i&gt;yet&lt;/i&gt;.&lt;/UL&gt;&lt;/b&gt;&lt;P&gt;&lt;br /&gt;The android SDK comes with three excellent documents - &lt;a href="http://developer.android.com/guide/practices/design/performance.html"&gt;Designing for Performance&lt;/a&gt;, &lt;a href="http://developer.android.com/guide/practices/design/responsiveness.html"&gt;Designing for Responsiveness&lt;/a&gt;, &lt;a href="http://developer.android.com/guide/practices/design/seamlessness.html"&gt;Designing for Seamlessness&lt;/a&gt; - that describe best practices. On the one hand, I'm not following them as closely as I could; the ExpandableListView at the heart of my UI is filled with homegrown and relatively expensive ViewEntry objects, rather than arrays as they suggest. On the other, it makes the code a lot easier to read, and ... don't optimize &lt;i&gt;yet&lt;/i&gt;. Thus far the app seems fast and responsive enough. Says me. We'll see what others think.&lt;br /&gt;&lt;LI&gt;HTTP GETs and POSTs. In case you'd like an example of how to do that fairly efficiently from Android, here ya go:&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;    public static HttpResponse DoHttpPost(String target, ArrayList&lt;BasicNameValuePair&gt; params)&lt;br /&gt;     throws IOException&lt;br /&gt;    {&lt;br /&gt;     try {&lt;br /&gt;      HttpPost httpost = new HttpPost(target);&lt;br /&gt;   UrlEncodedFormEntity entity = new UrlEncodedFormEntity&lt;br /&gt;    (params, HTTP.DEFAULT_CONTENT_CHARSET);&lt;br /&gt;   httpost.setEntity(entity);&lt;br /&gt; &lt;br /&gt;   //configure our request&lt;br /&gt;      HttpParams my_httpParams = new BasicHttpParams();&lt;br /&gt;   HttpConnectionParams.setConnectionTimeout(my_httpParams, CONNECT_TIMEOUT);&lt;br /&gt;   HttpConnectionParams.setSoTimeout(my_httpParams, SOCKET_TIMEOUT);&lt;br /&gt;   HttpClient httpclient = new DefaultHttpClient(my_httpParams);  //get http client with given params&lt;br /&gt;      httpclient.getParams().setParameter("http.useragent", Settings.AppName);&lt;br /&gt; &lt;br /&gt;   //upload set, let's try it&lt;br /&gt;      Log.i("com.rezendi.wtw.Util", "Preparing to post "+params+" to "+target);&lt;br /&gt;   HttpResponse response = httpclient.execute(httpost);&lt;br /&gt;   Log.i("com.rezendi.wtw.Util", "Sent POST, got " + response.getStatusLine());&lt;br /&gt; &lt;br /&gt;   //clear out the form data&lt;br /&gt;   entity.consumeContent();&lt;br /&gt;   return response;&lt;br /&gt;     }&lt;br /&gt;     catch (IOException ex)&lt;br /&gt;     {&lt;br /&gt;      Log.e("com.rezendi.wtw.Util", "Error posting "+ params +" to "+target, ex);&lt;br /&gt;      throw (ex);&lt;br /&gt;     }&lt;br /&gt;    }&lt;br /&gt;    &lt;br /&gt;    public static String Encode(String toEncode) throws UnsupportedEncodingException&lt;br /&gt;    {&lt;br /&gt;     return URLEncoder.encode(toEncode, "UTF-8");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static String DoHttpGet(String queryUri) throws IOException&lt;br /&gt;    {&lt;br /&gt;        HttpParams my_httpParams = new BasicHttpParams();&lt;br /&gt;  HttpConnectionParams.setConnectionTimeout(my_httpParams, Util.CONNECT_TIMEOUT);&lt;br /&gt;  HttpConnectionParams.setSoTimeout(my_httpParams, Util.SOCKET_TIMEOUT);&lt;br /&gt;  HttpClient httpclient = new DefaultHttpClient(my_httpParams);  //get http client with given params&lt;br /&gt;     HttpGet httpget = new HttpGet(queryUri);&lt;br /&gt;&lt;br /&gt;     try {&lt;br /&gt;      Log.i("com.rezendi.wtw.Util","Opening HTTP GET connection to "+queryUri);&lt;br /&gt;      HttpResponse response = httpclient.execute(httpget);&lt;br /&gt;      InputStream is = response.getEntity().getContent();&lt;br /&gt; &lt;br /&gt;         BufferedReader reader = new BufferedReader(new InputStreamReader(is), BUFFER_SIZE);&lt;br /&gt;         StringBuilder sb = new StringBuilder();&lt;br /&gt;         String line = null;&lt;br /&gt;         while ((line = reader.readLine()) != null) {&lt;br /&gt;          sb.append(line);&lt;br /&gt;         }&lt;br /&gt;         &lt;br /&gt;         is.close();&lt;br /&gt;         response.getEntity().consumeContent();&lt;br /&gt;         String results = sb.toString();&lt;br /&gt;         return results;&lt;br /&gt;     }&lt;br /&gt;        catch (IOException ex) {&lt;br /&gt;      Log.e("com.rezendi.wtw.Util", "Error GETting from "+queryUri, ex);&lt;br /&gt;      throw (ex);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;I recommend that you put these in one central Util class, as I did. Mind you, these are &lt;i&gt;static methods&lt;/i&gt; on Util, and not (yet) synchronized ... meaning not thread-safe. At the moment I don't think that's an issue; all my HTTP connections are very-low-bandwidth and resolve in seconds, so I can't imagine two such calls realistically colliding unless the phone user has very fast fingers indeed. But famous last words, right? Live by the thread, die by the thread.&lt;/P&gt;&lt;br /&gt;&lt;LI&gt;In both the Android and AppEngine cases, I started off by writing the tutorials that come with the SDK, and expanded the app from there. In both cases I'm not sure this was such a good idea. I dunno, maybe it's just my OO background, but if I'm writing a Notepad application, I'd like to have an object model with a Note object. But maybe that's not really the Android way. Performance, responsiveness, seamlessness, and all that.&lt;br /&gt;&lt;P&gt;I did like the way they calved all direct DB access off to a DbHelper class, and I've expanded that. Here's a hint; have all your DbHelpers inherit from a common ancestor. Yeah, I know, favour composition over inheritance, but still, here it will save you a lot of time, and give you a central repository for database creation scripts and such. Also, I'm still not exactly sure when you're supposed to close your database objects. In "onPause()"? Immediately after accessing them? Don't bother and let the Activity superclass handle it? It seems unclear.&lt;br /&gt;&lt;LI&gt;I'm not sure of the best way to handle error handling in Android. I like to have a common superclass for all my UI objects and inherit error handling from it, but that ain't gonna happen here, as my Activities inherit from various different places on Android's Activity tree. I guess I'll build some sort of ErrorHandler and pass exceptions to it where I think they're most likely to occur? Kind of annoying that there's no single piece of code you can write to catch all exceptions before they hit Android's own error-handling, but I guess that's the price you pay for their heavily compository app system.&lt;br /&gt;&lt;LI&gt;Moving back to AppEngine: the memcache service is awesome. Speeds up performance and cuts down on resource consumption immensely. However, for your own good, create a "ClearCache" web handler, for debug purposes. I've twice now spent fifteen minutes wondering why a problem wasn't fixed before realizing that the erroneous data was still in the cache and had not been replaced.&lt;br /&gt;&lt;LI&gt;Python's HTMLParser is very useful. However, it seems to have weird problems with ampersands. I &lt;i&gt;think&lt;/i&gt; I've now end-run around those problems, in a clumsy kludgy way, but I fear they too may yet crop up again.&lt;br /&gt;&lt;LI&gt;This project has included just enough JavaScript (maybe 100 lines) that I wonder if I should have used JQuery, and just little enough that I conclude that I probably shouldn't. Next time, maybe.&lt;br /&gt;&lt;LI&gt;I do believe that's all for now. See all y'all in beta, if not before.&lt;br /&gt;&lt;/UL&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-3308177959918236566?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/3308177959918236566/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/06/i-am-alpha-dog.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/3308177959918236566'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/3308177959918236566'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/06/i-am-alpha-dog.html' title='I am the alpha dog!'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-400222576169266578</id><published>2009-06-07T19:37:00.000-07:00</published><updated>2009-09-06T08:21:50.120-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GQL'/><category scheme='http://www.blogger.com/atom/ns#' term='AppEngine'/><title type='text'>GQL blues, or, the bloom is off the rose</title><content type='html'>So I've been doing lots of little stuff of late. I wrote my own Preferences edit screen, only to find that Android comes with &lt;a href="http://androidguys.com/?p=2038"&gt;an automagic one&lt;/a&gt; (sigh, although, I also wish this had been more obvious in the documentation.) I explored most of the mysteries of ExpandableListView, which is a pain to use, but would be an even bigger pain not to use.&lt;br /&gt;&lt;br /&gt;And I've built the basic spine of my application - an Android app talking to an AppEngine service, which takes a few well-specified requests and feeds back a few tightly structured (and low-bandwidth) responses. The smartphone app never talks to WikiTravel directly; instead it goes through AppEngine, which can a) do all the heavy lifting, b) log and track what's going on, c) cache data more intelligently, d) provide another layer of control and indirection.&lt;br /&gt;&lt;br /&gt;It's actually working pretty well; I'm updating WikiTravel successfully. (Well, from the emulator. I'm going to buy a real phone next week, and I expect to unearth more problems with the app when I do...) The UI is sinfully ugly, and right now it can only show you the well-formatted kind of WikiTravel listings to update, and there are a bunch of niggling things to fix and hardcoded strings to move to R.string and kludges to klean up, but by and large I'm pretty pleased with the progress.&lt;br /&gt;&lt;br /&gt;However. I have found the first thing about the Android-AppEngine stack about which I have serious reservations. I'm talking about AppEngine's "GQL" data storage.&lt;br /&gt;&lt;br /&gt;Now, on the one hand, this object database is quite cool. You can create, save, and write simple SQL-like queries for objects very easily. It reminds me, pardon me while I date myself, of the good old days of working with GemStone for Smalltalk, a wonderfully elegant (but ahead of its time, and hence cripplingly slow) object database for the world's purest OO language.&lt;br /&gt;&lt;br /&gt;But if memory serves you could do a lot more with Gemstone than GQL. Especially when it comes to queries. Now, I'm biased from years of experience with SQL databases - I already know how to make them do what I want, whereas with GQL I have to work it out from a fairly thin foundation - but even so, there is much to be desired.&lt;br /&gt;&lt;br /&gt;For instance: you can only have one inequality operator (eg "height &lt; 5") in a query. Which means that "height &gt; 3 AND height &lt; 5" is actually &lt;i&gt;impossible to perform&lt;/i&gt;. But wait, there's more; if there's an order by clause as well, it &lt;i&gt;must&lt;/i&gt; refer to the field that uses the inequality operator. So "height &lt; 5 ORDER BY emailAddress" is similarly impossible. Oh yes, and fetching more than 1000 rows at a time is equally impossible. As is counting all the rows that match a given WHERE clause. The official workaround for that last is to write a &lt;a href="http://code.google.com/appengine/articles/sharding_counters.html"&gt;Sharded Counter&lt;/a&gt;, which, I think you'll agree, is orders of magnitude more complex than writing "SELECT COUNT(*)".&lt;br /&gt;&lt;br /&gt;In general, getting around these restrictions means restructuring your object model heavily ... if you can get around them at all. OK, you denormalize databases for performance, and SQL has its quirks and blind spots too, but jeez, this is way past that: it's more like getting a car and being told "Listen, the steering wheel only turns right, so you have to make three right turns every time you want to go left."&lt;br /&gt;&lt;br /&gt;I'm sure they have their reasons. Scaling being one. And for an app like mine, where the data-storage requirements are pretty simple and straightforward, this all works fine. But if I was building something seriously data-intensive? I would think not just twice but thrice before using AppEngine, at least until it supports a more fully-featured datastore.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-400222576169266578?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/400222576169266578/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/06/gql-blues-or-bloom-is-off-rose.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/400222576169266578'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/400222576169266578'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/06/gql-blues-or-bloom-is-off-rose.html' title='GQL blues, or, the bloom is off the rose'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-3393223851543487867</id><published>2009-06-01T21:46:00.000-07:00</published><updated>2009-06-02T08:33:13.369-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AppEngine'/><category scheme='http://www.blogger.com/atom/ns#' term='zipme'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>zipme, baby</title><content type='html'>So I've finished the first crude version of my middleware and uploaded it to AppEngine. Looks like I'm going to have to upload the indexes by hand, though - I ran it through its paces before uploading it, but got a &lt;i&gt;NeedIndexError: no matching index found&lt;/i&gt; when I tried to run it on appspot.com. Oh well. No biggie. I'll do that tomorrow and then go back to my Android app.&lt;br /&gt;&lt;br /&gt;But mostly I wanted to tell you about a fun little app called zipme. AppEngine doesn't let you directly examine the source of the files you've uploaded. However, someone named "manatlan" wrote "zipme", a single python file that you add to your root directory so that you can subsequently download the entirety of your source code, zipped, from AppEngine. See &lt;a href="http://groups.google.com/group/google-appengine/msg/aa5f0e44d14116ee?pli=1"&gt;here&lt;/a&gt;. (It's configured so you have to be logged in as admin, in case you don't wanna show your source to the world...)&lt;br /&gt;&lt;br /&gt;&lt;i&gt;eta:&lt;/i&gt; spoke too soon - the indexes are now up n' running. However, JavaScript form handling is not. Well, this is why you deploy early and deploy often, so that you don't get bit by it at the last minute. Goin' on a bug hunt, brb...&lt;br /&gt;&lt;br /&gt;&lt;i&gt;etaa:&lt;/i&gt; in case you're curious, I realized after five minutes that the culprit was neither AppEngine deployment nor my code: it was the NoScript in my browser.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-3393223851543487867?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/3393223851543487867/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/06/zipme-baby.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/3393223851543487867'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/3393223851543487867'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/06/zipme-baby.html' title='zipme, baby'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-4170127334209041268</id><published>2009-05-30T14:36:00.000-07:00</published><updated>2009-05-30T19:16:56.412-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gaeunit'/><category scheme='http://www.blogger.com/atom/ns#' term='AppEngine'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>a satisfied customer</title><content type='html'>I strongly endorse (at least thus far) the GAEUnit Google AppEngine unit-testing framework, available (for free) &lt;a href="http://code.google.com/p/gaeunit/"&gt;here&lt;/a&gt;, and insanely easy to use.&lt;br /&gt;&lt;br /&gt;Also, don't be a total dummkopf like me and name your initial test file "unittest.py". Insert headdesk sounds here. Fortunately I realized the problem after a mere five minutes of staring at bewildering error messages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-4170127334209041268?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/4170127334209041268/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/05/satisfied-customer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/4170127334209041268'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/4170127334209041268'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/05/satisfied-customer.html' title='a satisfied customer'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-6123539608515307405</id><published>2009-05-29T12:09:00.000-07:00</published><updated>2009-05-30T19:17:03.229-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaScript'/><category scheme='http://www.blogger.com/atom/ns#' term='XMLHttpRequest'/><category scheme='http://www.blogger.com/atom/ns#' term='callback'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>Ajax, king of Salamis, son of Telamon and Periboea. No, wait. That other Ajax.</title><content type='html'>So I've been playing around with the web interface to my middleware lately.&lt;br /&gt;&lt;br /&gt;(The basic information stream for the app, at least initially, is "Android uploads GPS data, some brief notes, and maybe a picture to the middleware; middleware emails reminder to user to do something with it; user subsequently logs in to web site, conceivably from Android's browser, and uses previously uploaded data to update WikiTravel." I'm gonna add a more direct route that doesn't involve the web site for simple updates, but 'remind me to write this up in more detail later' seems like a more useful function, thanks largely to phone screen sizes.)&lt;br /&gt;&lt;br /&gt;Anyway, I stopped writing code just as Ajax - that's Asynchronous Java/XML, more or less, although it has a highly fungible definition - began to take over the web world and begat Web 2.0. So as a mere user, I've always been pretty impressed by it. Saving and loading data within the browser? Cool! Must be tricky!&lt;br /&gt;&lt;br /&gt;It ain't. On the contrary, it's really easy. Oh, I can see how you could tangle yourself up in knots with it, but for basic "go send stuff to server / revamp this piece of this page" stuff, Ajax is totally straightforward. Looks like it's often far simpler than doing everything on the server, even.&lt;br /&gt;&lt;br /&gt;I did solve one microproblem, so I'll post some code. Ajax is built around an XMLHttpRequest object, which you basically instruct to "go send this request to the server, then get back to me." You then set a callback function XMLHttpRequest uses to get back to you. But I wanted to pass a parameter to my callback function. I wasn't clear on the best way to do this, so I googled, but while there were a few solutions out there, frankly, I think this one I came up with is more elegant. (And I'm sure I'm far, far from the first to work this out.)&lt;br /&gt;&lt;br /&gt;Basically, you use the fact that JavaScript treats functions as objects, and write a function to create your callback function on the fly. Sound complicated? It really isn't. Here's JavaScript code to update/expand div with new data from the server:&lt;br /&gt;&lt;br /&gt;&lt;PRE&gt;&lt;br /&gt;      var http;&lt;br /&gt;&lt;br /&gt;      function getResponseFunction(divName) {&lt;br /&gt;        return function() {&lt;br /&gt;          if (http.readyState==4) { //4 means "done!"&lt;br /&gt;            var element = document.getElementById(divName);&lt;br /&gt;            element.innerHTML = http.responseText;&lt;br /&gt;            element.style.display='block';&lt;br /&gt;          }&lt;br /&gt;        }&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      function fillDiv(divName) {&lt;br /&gt;        var element = document.getElementById(divName);&lt;br /&gt;        http = new XMLHttpRequest();&lt;br /&gt;        http.onreadystatechange=getResponseFunction(divName); // customized callback function&lt;br /&gt;        var targetUrl="/expandDiv?divName="+divName; // a call to this URL will return the HTML with which we'll fill the div&lt;br /&gt;        http.open ("GET", targetUrl, true);&lt;br /&gt;        http.send(null);&lt;br /&gt;      }&lt;br /&gt;&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;Voila. Piece o' cake.&lt;br /&gt;&lt;br /&gt;(Yes, this project has now expanded to include Java, Python, &lt;i&gt;and&lt;/i&gt; JavaScript/DHTML/Ajax code. Hey, it keeps me from getting bored.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-6123539608515307405?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/6123539608515307405/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/05/ajax-king-of-salamis-son-of-telamon-and.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/6123539608515307405'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/6123539608515307405'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/05/ajax-king-of-salamis-son-of-telamon-and.html' title='Ajax, king of Salamis, son of Telamon and Periboea. No, wait. That other Ajax.'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-259176237881102296.post-9206685686033565860</id><published>2009-05-27T21:39:00.000-07:00</published><updated>2009-05-30T19:17:25.235-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='AppEngine'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='Google'/><title type='text'>On cloud computing nine. Well, maybe three.</title><content type='html'>I've finished the bare-bones functionality of the Android app I'm working on, and have gone back to the AppEngine middleware. The purpose of the app, in case you're curious, is to make it easy to update WikiTravel from your phone, complete with location data and/or a picture.&lt;br /&gt;&lt;br /&gt;AppEngine is remarkably easy to work with. And as my friend Martin pointed out, it's now available in both Java and Python (although the Java is still in "Early Look" status.) I'm working in Python for the sake of variety, and also because the advantages of Java in this context are not immediately obvious.&lt;br /&gt;&lt;br /&gt;Anyway, I don't have as much Python experience as Java, but that hardly matters, because it's all very straightforward. I'm having a permissions problem getting the location of an IFrame, which means I may have to make things a little more annoying for the user than I'd like, but that's an XSS browser-security issue not a development issue.&lt;br /&gt;&lt;br /&gt;The basics of a web application - getting data from the database and request, displaying it to the page, and saving it as and when needed - are all perfectly straightforward; so much so that I'm not even going to bother posting any code here, for once, because none of it seems particularly interesting. Which is a good thing. It means can focus on what you want to do, unlike the bad old days, where you spent a hefty fraction of your time worrying about how you're going to do it.&lt;br /&gt;&lt;br /&gt;(I'm sure I'll hit such a wall at some point, and fear not, when I do I will whine about it at logorrheic length.)&lt;br /&gt;&lt;br /&gt;AppEngine also gives you lots of freebies. Sending emails easily, for once. Scalability and data integrity, for two. Goodbye, J2EE deployment descriptors; that heavy lifting now happens pretty much behind the scenes, although you do have to group objects affected by single database transactions together in advance.&lt;br /&gt;&lt;br /&gt;You also get automatic seamless user handling, so long as you use Google Accounts as your userbase. Caveat; it's easy to link to a login page, but I haven't quite worked out how to integrate a login form into your own pages. Even so, this is pretty brilliant. It means your site comes with all the user headaches - login, logout, password reminder, sending them emails, etc. - pre-handled, saving you time and grief. It also means that if you use it, which I am for the sake of convenience, you lock yourself even further into Google's infrastructure, and expand the tentacular remit of Google Accounts. Good thing they're not evil, eh?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/259176237881102296-9206685686033565860?l=www.rezendi.com%2Fpa' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/9206685686033565860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.rezendi.com/pa/2009/05/on-cloud-computing-nine-well-maybe.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/9206685686033565860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/259176237881102296/posts/default/9206685686033565860'/><link rel='alternate' type='text/html' href='http://www.rezendi.com/pa/2009/05/on-cloud-computing-nine-well-maybe.html' title='On cloud computing nine. Well, maybe three.'/><author><name>Jon</name><uri>http://www.blogger.com/profile/17475458068193351080</uri><email>jon@rezendi.com</email><gd:extendedProperty xmlns:gd='http://schemas.google.com/g/2005' name='OpenSocialUserId' value='17575295583420395878'/></author><thr:total>0</thr:total></entry></feed>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
