Showing posts with label iPhone. Show all posts
Showing posts with label iPhone. Show all posts

Friday, September 3, 2010

Integrating Core Data into your Three20 App

The Three20 library for iPhone app development is pretty cool - it take care of a lot of the 'mundane' code that you might need to write to accomplish basic tasks. Apple really should include some of the functionality of Three20 in the iPhone SDK itself, specially the Network stack.

As I built a more advanced app using Three20, I realized that it would be great to be able to persist to a database to make loading quicker when state on the server is unchanged. I needed some pretty basic Core Data integration in my app, and it was a little annoying that Three20 didn't have a Core Data-backed implementation of TTModel built into it. It seems like a lot of people want this, but there isn't any good post about it I could find. Jeff Verkoey, who currently maintains Three20, started writing a Core Data based tutorial but didn't get around to actually doing much with it. And there are a few threads in the Three20 Google group but nothing too comprehensive.

Having built this for my app, I figured I would post some more concrete information here for others who might find it helpful. I really want to just contribute a clean cut of this code as a TTCoreDataModel with unit tests to the Three20 source, but I don't have the luxury to spend time on it currently.

So here's a quick step-by-step on how to modify your existing Three20 based app to add basic Core Data support. The specific example I use fetches data from a server using TTURLRequestModel and writes it out to SQLite using Core Data entities. When displaying data to the user, it fetches data from Core Data using a NSFetchedResultsController, which efficiently interacts with Core Data.

Step 1:
Add CoreData.framework to your list of dependencies. Then, add the Core Data stack. Usually, you would do this in the AppDelegate. Jeff's AppDelegate files have this implemented, if you want an example. In my case, I wanted different databases for different Models, so I had these in my TTModel implementations.

Step 2:
Create your model. Add a new file of type "Data Model" in XCode (available in the 'Resource' section). Make sure the .xcdatamodel file gets added to your Target so that it's compiled into your binary. Look here for information on using the modeling tool. Once you've created your model, import the code for the Entity objects into your project. Now you're ready to start using Core Data.

Step 3:
Update your TTURLRequestModel implementation. Let's assume you're simply downloading some JSON and want to write it out to Core Data as an Entity you created in Step 2. Update your (void)requestDidFinishLoad:(TTURLRequest*)request implementation of TTURLRequestDelegate as follows:




A few notes about the code above:
  • DBResource is my simple entity containing an "id" and a "name".
  • The NSAutoreleasePool helps clean up memory allocated in this tight loop here
  • -reseting the context makes sure that the data faults in as needed instead of staying in memory when you read it using the NSFetchedResultsController
Step 4:
In your TTTableViewDataSource implementation, create a NSFetchedResultsController and configure it to fetch your Entities as needed. Then, override the (id)tableView:(UITableView*)tableView objectForRowAtIndexPath:(NSIndexPath*)indexPath method to return a TTTableItem built using the Entity you fetched from Core Data




Now, your TTTableViewDataSource is configured to read results from the DB you created and display them in your view. Remember that tableViewDidLoadModel can be called multiple times, so clean up any previously allocated NSFetchedResultsController if you need to.

Step 5:
Implement your logic to delete/update your Core Data entities. Depending on how your app uses data persistence, you want to ensure that you update/delete your entities from Core Data. For instance, if you always want to fetch data from the server and load them into Core Data on first run, then update Step 3 to first delete all entities. If you wish to show local data when available but fetch when not, then update Step 3 to not hit the server before checking locally. When deleting entities, remember that Core Data only lets you delete a single entity at a time (so you must fetch all and loop through each to delete it). If you want DROP TABLE ... semantics you're going to need to maintain different NSManagedObjectContent and NSPersistentStoreCoordinator for each such table, and then simply delete the database file.

So, this isn't too hard really. Assuming you're familiar enough with building your Three20 app, my notes above should help you get started with integrating Core Data into your app. My app uses a TTSectionedDataSource, and with a few tweaks to NSFetchedResultsController it's simple to do that too.

Wednesday, May 19, 2010

Showing the Cancel button in UIActionSheets on iPad

In it's iPad Human Interface Guidelines Apple provides several guidelines about how user experience should differ between iPhones and iPads. One such element that should behave differently is the UIActionSheet.

Apple recommends that cancel buttons not be shown for action sheets on iPad:
Because taps outside the popover dismiss the action sheet without selecting an item, this results in a default way to cancel the sheet. Including a cancel button would therefore only cause confusion.
But the guidelines do allow exceptions to this rule:
However, if you have an existing popover and are displaying an action sheet on top of other content using an animation, a cancel button is still appropriate.
So you would think following this guideline is as simple as not specifying a cancel button in most cases, and only specifying a cancel button when you have carefully considered the exception case, right? Wrong. Even when you do specify a cancel button, it looks like the iPad runtime automatically always hides it.

So how do you provide a cancel button in the exception case? You could just provide another normal button that has cancel behavior, but that's not ideal because you really want the difference in appearance that UIActionSheets on iPhone provide by default. After some tinkering, the best solution I could find was to set the actionSheetStyle property of the UIActionSheet to UIActionSheetStyleBlackOpaque.

I can speculate why this works, but I don't know for certain so I'd rather not. If you know of a better/more correct way to do this, please do leave a comment.

I am surprised that Apple was mindful enough to list the exception case, and yet careless enough to not provide documentation on how to handle this case - they even seem to have forgotten to document that the iPad automatically overrides user-provided cancel buttons by default.

Tuesday, July 1, 2008

Some thoughts on Apple's Push Notification Service

Last month, at WWDC Steve Jobs announced iPhone 3G. Very cool. I think the iPhone is a game changer. Enough people have dissected the iPhone 3G that I'll skip re-stating what everyone knows already.

One of the more interesting announcements Jobs made at the WWDC Keynote was the iPhone Push Notification Service. In short, it's a single channel from the cloud to each iPhone, and can be used by software publishers (in addition to Apple) to send signals to apps on the iPhone without having to worry about creating their own custom channel to the cloud or using polling schemes. This sounds like a win-win for both publishers of iPhone apps and for Apple.

Given that I work in a similar space, this was interesting to me. We had been thinking along similar lines and have in some ways even taken the concept even beyond what Apple announced. I'll save the details/designs of what I'm working on for another post.

The technology Apple touted sounds a lot like Exchange Direct Push/Always-Up-To-Date, except that it opens up the channel from cloud->phone to third parties instead of the exchange model where the only publisher is Exchange and the only consumer is the Outlook Mobile client.

The design challenges of opening up a channel of this sort to third parties are:
1) Delegated authentication to allow third parties to use their notification channel
2) Routing notifications to the correct iPhone
3) Introduction of a single point of failure for most apps on all iPhones (kinda similar to AT&T going down - if Apple's service goes down all cloud-dependent apps are 'bricked' unless they have some form of background polling, which would defeat the purpose of the single notification channel since Apple touted battery life as their primary motivation. BTW, I don’t believe that is their primary motivation – more below).

One interesting feature they did not talk about, and I believe that they won’t add anytime soon, is a 2-way communication channel to allow for messages from the phone to the publisher. The notification channel is likely 2 way inherently, but communication from the phone is only ever seen by Apple.

Here are my guesses on how they deal with the above three:

There is only one way to get apps on the iPhone (assume non jail-broken iPhones) – the Apple App Store. To publish to the store, publishers share revenue with Apple. I’m certain there is some kind of certification that happens at this stage to ensure that apps are well behaved. Once certified, the publisher probably gets a ‘security token’ to communicate with Apple’s Notification Service if they want to use it. Further, my guess is that each app published by each publisher might have a unique security token. I presume that the content of Notification that publishers push through the channel would be encrypted and opaque to Apple – it would be very bad if that’s not the case. However, at least 2 pieces of information must be known to Apple – who is publishing and whom is the Notification going to.


My guess is that Apple is told what phone to send a notification to by publishers, versus figuring out itself where to route the Notification. In other words, the subscription is held by the publisher and not by Apple. The reasons I think this is how it must be is:
- Apple would not like to be responsible for tracking subscriptions of each app on each iPhone. It would mean they have a ton of subscriptions to manage, plus they need to perform lookups in their cloud for routing notifications to the right phone. By letting publishers deal with this, they are no longer responsible for the costs incurred of maintaining these subscriptions. Apps that create several hundred subscriptions cost the publisher, and the cost of performing lookups are also incurred by the publisher. This makes Apple's SLA much simpler.
- Publishers (I hope) would not want Apple knowing data about their customers.

#3 is interesting – the service needs to be robust again badly behaved publishers. Their first line of defense here is probably their certification program. Apps must not do wild-card broadcasting, must not poll, must have an upper limit on Notification payload sizes, etc. Note that the announcement yesterday was only to allow 3rd parties to support 3 types of Notifications – ‘badges’, ‘audio alerts’ and ‘text alerts’. By nature, the first two should be tiny in size, the third probably has an upper limit on size of text. Their second line of defense is probably real time metering and throttling. Apps must be self metered or will be throttled by Apple. Publishers are motivated to adhere by these guidelines because Apple controls publishing their app and could impose restrictions on downloads of the app in the App Store.

Regarding Apple’s stated motivation for the Notification Service – I suspect they have more interests than just “preserving battery life”. By controlling the App Store and this Notification channel, Apple now has direct insight into a ton of data that can be mined – what apps are most popular, what apps are most chatty, what app is the ‘flavor of the month’ by usage and not just by download metrics, most active users, etc. They can use this data both ways – to make special licensing deals with specific app makers they detect are 'hot' and create popular apps (popular download is different from popular usage) and also to directly target ads/other apps to consumers.

Other's have posted on how Apple might be building something similar for the Mac. They probably are, but the motivation for publishers to use such a channel is greatly lesser in case of Macs. There is no single channel of publishing apps for the Mac, although they do make it easier. As a publisher, the primary attraction of using something like this would be to not have to build something yourself, but I think the need to share revenue with Apple, get certified and distributed via official Apple channels only and use Apple as the middle-man for what essentially shouldn't be any of their business might act as deterrents.