Tag Archives | iphone

Now is the time to downgrade your AT&T SMS plan

I’ve always had the $30 unlimited family text plan with our iPhones, I’ve switched to using SMS replacement apps with my most frequently texted contacts. I used to average about 700 messages a month in the plan, but the past several months I’ve gone under 200. Paying $30 a month for SMS is pretty ridiculous, worse yet, when I text between my 2 lines in my family plan, it counts on each line, while it’s free to actually place a voice call to any AT&T subscribers. I believe voice calling uses more network resource than an SMS.

So this Monday I downgraded the family text plan to the 200 messages plan for $5, going from $30 to $10 total. Here are the apps I use to replace SMS, there are many more, but these 2 use your existing mobile number as your identifier, which makes them real SMS replacements and allows easy contacts discovery.

  • WhatsApp: popular in Taiwan for some reason, $1
  • KakaoTalk: popular in Korea, free app.

In addition, I’m a huge BeejiveIM user, it’s the best IM app and definitely worth the $10. I’ve also dialed down my SMS from Twitter with push notifications from Boxcar and the Twitter app. Boxcar notifications consistently arrive before Twitter SMS when I had both turned on.

Looks like AT&T will be discontinuing the $5 SMS plan, so if you’ve been procrastinating to downgrade, now is the time to do it.

Leaked: New ATT text messaging plans in preparation for Verizon iPhone | Gear Live.

Tags: , ,

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.

Tags: , , , , , , , ,

[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;
}

Tags: , , , , ,

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.

Tags: , , , , ,

From BusySync to CalDAV (iCal + iPhone + GCal)

UPDATE: these instructions are quite old, use this instead: http://www.google.com/support/calendar/bin/answer.py?hl=en&answer=99358#ical

I’ve moved everything to Google Apps for Domains a long time ago, so my personal and work mails, contacts, and calendars are all on my GApps accounts. All my consulting clients are also on GApps. Using GApps, iCal and Addressbook on my Mac, and my iPhone, I got decent PIM integration between phone, mac, and cloud. (it’s come a long way since my PIM post in 2005). Before Google enbaled CalDAV, I used BusySync to sync iCal and Google Calendars, it worked well, and my phone synced with iCal and it in return synced with GCal. This worked well enough, but now iPhone OS 3.0 supports CalDAV, it is possible to have my calendars on the phone synced with GCal directly, without having to go through iTunes. (I must mention that BusySync does a lot more than syncing iCal and GCal, but that was the only feature I used and also the only reason why I paid for it).

So the basic idea is, remove BusySync, setup iCal and phone to sync with GCal separately via CalDAV, stop iPhone calendar syncing in iTunes. I have quite a few GApps accounts and I have multiple calendars in my personal account. My passwords are also randomly-generated with 1Password and they are 50-character long, so I needed an easy way to get all these CalDAV accounts configured on the iPhone. (even with copy-and-paste it can still get really tedious very quickly). After a little bit of Googling, I found these:

  • iPhone Configuration Utility – With this I can setup a configuration profile with all my CalDAV accounts, and then install the profile onto the phone, this is the only way to configure anything on an iPhone, period. You can find detail instructions with screenshots here.
  • Calaboration – an opensource tool for Mac to help setup GCals CalDAV accounts in iCal, without this it is a bit more work to figure out the URIs for your additional calendars.

It took only about 15 minutes to get CalDAV setup for iCal and the phone, and now when I add something from the phone it shows up on my mac without having to sync the phone in iTunes.

Note that all my calendars were already on GCal in my GApps accounts, if you have a calendar in iCal that is not already on Google, CalDAV won’t really help you there. I’m sure you can figure out how to import it into GCal though.

The next thing I really want is, obviously, to get everything pushed to the phone. I have quite a few mail and CalDAV accounts on my phone, and pulling every 15 minutes for all accounts probably eats up quite a bit of battery. I’ve been using msgpush.com to get push notifications via ActiveSync for my personal mails, but it doesn’t work all that well. Also, msgpush.com doesn’t even work after I changed my password to a 50-character-long string. The GPush app looks more promising, so hopefully Apple approves it. In Apple Mail on my mac I use IMAP IDLE and sorta does push, but that doesn’t work very well either! In fact, I think this is why msgpush hasn’t worked well for me, IMAP IDLE is a pretty old spec and it sucks. Right now even CalDAV accounts are doing sync every 15 minutes in both iCal and on my phone, and I don’t think the technology itself has any push capability built in. (It is an extension to WebDAV afterall). I’m beginning to think ActiveSync might not be such a bad idea, and this is a lot coming from a guy who generally don’t like or use anything by Microsoft.

Tags: , , , , ,