Category Archives: Tech

Speeding up Core Data-based UITableViewController

It is pretty common for an iPhone/iPad app to make an API call to a server, get the JSON response data back, parse that data, and display it in a table view. The usual way to do this looks like this:

- (void)apiCall {
	NSString *urlString = [[NSString alloc] initWithFormat:@"%@/some_models/some_action.json", apiEndpoint];
	NSURL *url = [[NSURL alloc] initWithString:urlString];
	NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
	[request setHTTPMethod:@"GET"];
	[NSURLConnection connectionWithRequest:request delegate:self];
	[url release];
	[urlString release];
}

This fires off the API call asynchronously, and then you implement some delegate methods like this:

- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
	responseData = [[NSMutableData alloc] initWithCapacity:[response expectedContentLength]+100];
}

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
	[responseData appendData:data];
}

- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
	NSString *jsonString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
	[self dictionaryToCoreData:[jsonString JSONValue]]; //this parses the JSON data and persists into Core Data
	[jsonString release];
	[responseData release];
}

This approach works fine, but when you run it on the real devices, you might notice that the table view locks up when you go between the navigation flow. Basically the table view ignores user inputs until everything above is finished. This is because everything is performed on the main thread and it locks up the UI. The asynchronous NSURLConnection method used above doesn’t like to be used in a background thread, and there is really no reason to do things asynchronously if you’re working in the background, conveniently, there is a +sendSynchronousRequest method that waits until we get the response and data in NSURLConnection. To perform the above API call in a background thread, the code is actually much simpler:

- (void)apiCall {
	[self performSelectorInBackground:@selector(backgroundApiCall) withObject:nil];
}

- (void)backgorundApiCall {
	@synchronized(self) {
		NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; // Top-level pool

		NSString *urlString = [[NSString alloc] initWithFormat:@"%@/some_models/some_action.json", apiEndpoint];
		NSURL *url = [[NSURL alloc] initWithString:urlString];
		NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
		[request setHTTPMethod:@"GET"];
		
		NSURLResponse *resp;
		NSError *error;
		NSData *responseData = [NSURLConnection sendSynchronousRequest:request returningResponse:&resp error:&error];
		// you should probably do some error handling here
		NSString *jsonString = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
		[self dictionaryToCoreData:[jsonString JSONValue]];
		[jsonString release];		
		[url release];
		[urlString release];
		[pool release];
	}
}

Now, when the table view is first loaded, it displays the stale data in Core Data, the API call is fired off in the background, and when we get data back from the call the table view is updated with the new data. Stale data is better than locked up UI.

Notice the very first time you run the app the table view will be empty until the background thread finishes, if this bothers you, you can preload the database with sample data. In our app we can’t really do this as the data is unique to the user, but it might make sense for you to ship your apps with pre-loaded data.

[Reachability reachabilityForLocalWiFi] crash with OS4 SDK

If you’re using Apple’s reachability framework and compiling with OS4 beta SDK, you might get a crash like this:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[Reachability<0xf37d0> init]: cannot init a class object.'

You can fix it by commenting out the [super init] line in + (Reachability*) reachabilityForLocalWiFi in Reachability.m:

+ (Reachability*) reachabilityForLocalWiFi;
{
	//[super init];
	struct sockaddr_in localWifiAddress;
	bzero(&localWifiAddress, sizeof(localWifiAddress));
	localWifiAddress.sin_len = sizeof(localWifiAddress);
	localWifiAddress.sin_family = AF_INET;
	// IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
	localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM);
	Reachability* retVal = [self reachabilityWithAddress: &localWifiAddress];
	if(retVal!= NULL)
	{
		retVal->localWiFiRef = YES;
	}
	return retVal;
}

re: section 3.3.1

In developing an iPhone app for Pullfolio, we initially went with Titanium as Ray had used it before at Intridea, and we are familiar with web technologies. With that we were able to almost finish the app within a week. However, we quickly found limitations of the framework, more specifically, it was non-trivial to make a Photos.app-like thumbnails view without using webview, and to make a single photo view with slideshow that supports pinch/zoom gestures, lazy-loading different versions of images off the Internet on-demand, proved to be close to impossible without us implementing more native UIKit components for the framework.

So we learned and switched to Objective-C, and will never look at a cross-compile option for iPhone and iPad apps again. I agree with @gruber and Jobs, cross-compile solutions mostly yield low quality apps. Also, it always takes a short while for 3rd party solutions to catch up with the latest SDK features, so you will be a step behind your competitors. I think cross-compiling is okay if you’re a consultant building apps for clients who wanted iPhone apps. But if you’re a app developer who wants to write something original and something that provides rich UX, going native the the only way to go. Learning new languages, tools, and frameworks is part of the business of being developers. (we all did switch from php to rails/django, or vb to c#, cvs to svn and then to git).

Also, if you don’t like the app store limitations, you can always develop for the web. With the exception of Flash, Safari on iPhone and iPad works great, instead of using Facebook.app I just hit it in Safari, it works much better than their app IMO. A ton of sites have already made themselves iPad-friendly.

iPad apps

I spent the most of the past 2 days playing with my iPad. The apps I’ve tried so far are pretty amazing. Other than the obvious apps such as Netflix and ABC Player, here are some apps that I find really useful:

GoodReader for iPad ($0.99 limited time intro price, iPad-only). This app lets you move and read large PDF documents on the iPad, it has an iPad-optimized reader. You can search, there is even a button to dim the brightness of the display. To transfer files into GoodReader all you have to do is to go into the app and switch on WiFi transfer, and then you can connect to it in Finder on a Mac. You can also transfer files in iTunes, or link up your Google Docs, Box.net, Dropbox (I’m a huge user of Dropbox!) accounts and download from them. You can also get to any WebDAV servers inside the app. You can even connect to your Gmail or other email accounts and download your attachments into GoodReader!

skitched-20100407-114552.png
Uploaded with plasq‘s Skitch!
IMG_0014
Uploaded with plasq‘s Skitch!

You get this under the Apps tab in iTunes:

iTunes
Uploaded with plasq‘s Skitch!

Kindle (free, universal version for both iPhone/Touch and iPad) and iBooks (free, iPad-only). Books in iBooks look cool, the Delicious Library look-alike bookshelf is a nice touch. But Amazon has a much larger selection of books available for the Kindle. I’d held out not to buy a Kindle for so long, so it’s great to have ebooks options on the iPad. The Kindle iPad app dims the background and the book lids up the kid’s face when you use it at night.

skitched-20100407-122511.png
Uploaded with plasq‘s Skitch!

NewsRack ($4.99, universal version). On the iPhone I use Google Reader in Safari and it works okay, but it doesn’t work as well on an iPad, and an RSS reader with offline sync is important especially because this iPad is wifi-only. NewsRack can sync with my Google Reader account, and by that I mean it can grab my subscriptions, maintain read/unread data, and even share articles back at Google Reader. It also has sharing features to email article, add to Instapaper, Readitlater, Twitter, Delicious, etc. It’s a universal app so I get to use it on my iPhone as well, great deal for $4.99 IMO.

Instapaper Pro ($4.99, universal). I am a long time user of Instapaper, Tweetie has Instapaper support so I save a ton of links in tweets that I want to read later. The Instapaper Free app worked fine for me on the iPhone, but the Pro version is universal and is iPad-optimized. If you don’t use Instapaper you really should.

1Password Pro ($14.99, universal), or 1Password for iPad ($6.99, iPad-only). If you use a Mac, you have to use 1Password. I bought a family pack of the desktop version, and the Pro version after iPhone 3.0 to get the ability to copy password into clipboard easily. I think I paid a lot less than $15, and at some point it was even free for a short while. I am glad Agile Web Solutions did the right thing and made the Pro version universal (unlike Cultered Code with Things, which I’ll talk about later).

Photo Pad: Flickr ($3.99, iPad only). If you use Flickr heavily for photo sharing and storage, you will like this app. It basically helps you sync your photosets on Flickr onto the iPad and displays them in amazing resolutions on the iPad’s gorgeous IPS display. There is no slideshow feature, so you can’t use it as a digital photo frame, and you can’t search photos on Flickr by tags (you can search the photos already downloaded by tags though).

skitched-20100407-122659.png
Uploaded with plasq‘s Skitch!

Bloomberg for iPad (free, iPad-only, but there is an iPhone version too). This app pretty much turns your iPad into a Bloomberg terminal, well, there is no Bloomberg IM and it’s not really the real thing, but it’s pretty damn close, and it’s free!

skitched-20100407-125121.png
Uploaded with plasq‘s Skitch!
skitched-20100407-125022.png
Uploaded with plasq‘s Skitch!

SketchBook Pro ($7.99, iPad-only). This is the best sketching/drawing app on the iPad at the time of this writing. Really amazing app, definitely worth the $7, considering a comparable desktop sketching app will no doubt cost a lot more. I wish iPad was pressure sensitive though, it still can’t replace a Wacom tablet for serious illustrating or photo retouching work.

There is one app that pisses me off – Things for iPad. Things is my GTD app and I use it heavily. I bought a family pack ($75) for the desktop version , as well as the iPhone version ($10). Now instead of releasing a universal version upgrade, they wanted $20 for Things for iPad. $20 is just a bit much even though the app looks gorgeous and I’m sure it works really damn well. For now I’m going to run the iPhone version on the iPad until I can’t stand it anymore.

Pullfolio private beta! Here’s your invite!

If you’re a pro/semi-pro photographer and love Flickr, I think you will love Pullfolio. Pullfolio helps you create professional portfolio sites using photos in your Flickr account by tag(s) or photosets. For example, you can setup your photography site with these portfolios:

  • Wedding – it pulls all photos that are tagged pullfolio and wedding (or whatever tag(s) you specified when you configure the portfolio) from your Flickr account
  • Portraits – it pulls all photos from your portraits photoset on Flickr

You get the idea. You can also enter your blog URL, bio, and contact info, and they will be included on your site. You can also choose from a few themes. If you upgrade to the Pro subscription for just $15 a month, you will be able to point your domain to the site, add your analytics code for tracking, and if you want to get your hands dirty, you have the ability to completely customize your site. We plan to partner with designers to provide more themes, and if you need a designer, we can provide referrals to ones who understand how our themes work.

When you upload new photos to Flickr, you will just have to tag them or add them to a photoset and your portfolio website will have the new photos automatically.

You can check out my photography site to get an idea of how it works. I still have to setup my own portfolios properly though.

If this sounds like it’s something you want, I am giving out 100 invites, here’s a direct link to signup for our private beta:

Each account also comes with 5 additional invites upon signup.

A bit of background: as a heavy Flickr user, I’ve always wanted a photography site that pulls photos from my Flickr account. This way I can upload my photos to Flickr with the right tags or photoset, and it will automatically show up at my photography site. After not having a proper photography site for more than 3 years, there still isn’t a service out there that does what I want. It’s also quite shocking that I was able to get gigs by networking and my Flickr photostream (or “collections” rather) without a website. When Ray and I were brainstorming ideas of what to build, I selfishly suggested this idea. We got a prototype working within 2 weeks, however, to turn the idea into a real product that supports many users, themes, subscriptions, custom domains, while using behavior-driven development with 100% test coverage, was a significant effort.

Feel free to post any feedback at our UserVoice feedback forum. You can follow Pullfolio on Twitter to get updates on new features.

Mounting and un-mounting external drives without plugging/unplugging in OS X

I have a 500GB MyPassport drive I use to clone my Mac’s internal drive, it’s plugged into one of the USB ports behind my Apple Cinema Display. After I eject/un-mount the drive I don’t want to unplug and re-plug the USB cable to re-mount the drive, I can re-mount it in Disk Utility, but that’s still kindda annoying, so I looked and here’s how you do it in the command line.

When you have the drive mounted, do a df to see what the device is:

[ProBert:~] ayn% df
Filesystem    512-blocks      Used Available Capacity  Mounted on
/dev/disk0s2   976101344 427449800 548139544    44%    /
devfs                220       220         0   100%    /dev
map -hosts             0         0         0   100%    /net
map auto_home          0         0         0   100%    /home
afp_0TT7Xo1noyS900m5Am0Pyetl-1.2d000017 1949330784 1365867776 583463008    71%    /Volumes/Time Capsule
/dev/disk2s2                            1949330720 1365933248 583397472    71%    /Volumes/Backup of ProBert
/dev/disk1s3   976510944 427839352 548671592    44%    /Volumes/AYN MBP Clone

The device name is /dev/disk1s3 in my case. After that, you can mount and un-mount in Terminal easily by doing this:

[ProBert:~] ayn% diskutil unmount /dev/disk1s3
Volume AYN MBP Clone on disk1s3 unmounted
[ProBert:~] ayn% df 
Filesystem    512-blocks      Used Available Capacity  Mounted on
/dev/disk0s2   976101344 427449672 548139672    44%    /
devfs                221       221         0   100%    /dev
map -hosts             0         0         0   100%    /net
map auto_home          0         0         0   100%    /home
afp_0TT7Xo1noyS900m5Am0Pyetl-1.2d000017 1949330784 1365867776 583463008    71%    /Volumes/Time Capsule
/dev/disk2s2                            1949330720 1365933248 583397472    71%    /Volumes/Backup of ProBert
[ProBert:~] ayn% diskutil mount /dev/disk1s3
Volume AYN MBP Clone on /dev/disk1s3 mounted
[ProBert:~] ayn% df -h
Filesystem      Size   Used  Avail Capacity  Mounted on
/dev/disk0s2   465Gi  204Gi  261Gi    44%    /
devfs          111Ki  111Ki    0Bi   100%    /dev
map -hosts       0Bi    0Bi    0Bi   100%    /net
map auto_home    0Bi    0Bi    0Bi   100%    /home
afp_0TT7Xo1noyS900m5Am0Pyetl-1.2d000017 1949330784 1365867776 583463008    71%    /Volumes/Time Capsule
/dev/disk2s2                            1949330720 1365933248 583397472    71%    /Volumes/Backup of ProBert
afp_0TT7Xo1noyS900m5Am0Pyetl-1.2d000017  930Gi  651Gi  278Gi    71%    /Volumes/Time Capsule
/dev/disk2s2                             930Gi  651Gi  278Gi    71%    /Volumes/Backup of ProBert
/dev/disk1s3   466Gi  204Gi  262Gi    44%    /Volumes/AYN MBP Clone