(Phone) Application Developer,
Why does the phone screen on your application lock up when I try to type or push a button? Sometimes, I see the spinner, but it won't let me back out of the screen?
It annoys me, so I deleted your application.
- A. Former Customer
Do your applications do this?
It's a sad state of affairs. I've used plenty of applications on and off the phone. I rarely encounter an app written with a UI that screams perfection. Most of the time, applications are written with a UI as an after thought, with more concern placed on bug hunting than concern for the humans using the program. I'm not saying bugs shouldn't be hunted down and slain (that's what unit testing is for, really). What I am saying is that the User Interface, and only the User Interface, is what your customers see. It makes the first and last impression.
I've deleted applications from my phone after watching their launch screens, having never seen them run. Why? Really, really lousy UI. If the product development team couldn't take the time to make a good impression on me, to make the experience of using their product a pleasant (if not pleasurable) one, why should I bother with it? They don't care, why should I.
My greatest pet peeve (and one I share with the rest of the population) is far and away a non-responsive phone screen. When I hit a button or touch a keyboard key, the app had better react. I don't mean when the user enters text in a search box that the search results fill immediately. What I do mean is that when the application queries a back end server for data using the first three letters a user typed "myt", it had better not lock when the user wants to hit an "h"! Do the query in the background and make the screen I/O the primary activity.
If you find you have a long running operation that locks up the screen from user input (slow or delayed responsiveness) and you feel the urge to throw up an inactivity display (spinner), STOP, do not do it. Instead, redesign your screen and operation flow to move the computation into the background. You may need to re-architect your entire application, to do this. Yes, that can be a challenge. However, what's the alternative? An application delivered on time that irritates its users just because you didn't want to do the work.
For the record, inactivity spinners are fine things to have in an application. They signal to the user that the application is thinking really, really hard; so hard, it looks stuck. That's the nature of computation devices, it's the phone or computer's way of sticking up a (polite) finger and saying: "Just a moment while I work this out." However, just like a person, devices should be interruptible when a higher priority item comes along. Phone calls can interrupt your application at any time and you don't have a choice about responding to them. It's a good thing, too, because I bet most applications wouldn't bother to release control and using the phone would be a really horrible experience.
Apple's own applications suffer from this. Ironically, the AppStore app is horrible! If I've left the AppStore app on my phone in the "Update" tab, when I go back into the app, it locks up while checking to see if any of my apps need to update. I want to tab over to the search for a new app, but I have to wait for the stupid check (which could be run in the background) to complete before the application responds to my tab selections. Crappy!
On the other hand, the email app does a great job; it has to! Potentially, it has multiple message feeds, multiple message send queues, local data caching, updating of message status, etc. There's no way that application could not run operations in the background. It would be slower than molasses. One of the chief benefits of the phone (quality email on the go) would be unusable. In fact, I would venture to say that without the email application providing such an excellent user experience, the iPhone would never have been able to supplant Blackberry.
A great UI is that important.
So, what should you do? You'll have to do extra work, which means using threads, GCD (Grand Central Dispatch), or NSOperation queues. You'll have to be careful about using Core Data from different threads. You'll have to be careful about running graphics operations on the main thread. You'll have to handle UI operations promptly and never delay a screen update because the data is not ready. You're going to have to design and architect and think! If you don't do those things, you're going to have a crappy application. Your application, your work, says more about you than you realize. It says more about you than about the platform on which it runs. You will be judged, and you may be found wanting.
Don't write crappy apps.
Sunday, April 1, 2012
"If you have a background in database management ..., don't worry: if you use a SQLite store, Core Data automatically creates the intermediate join table for you."
- Core Data Programming Guide > Relationships and Fetched Properties > Many-To-Many Relationships
You've been hacking on iOS, and now you need persistent storage. You know SQL, you want to use SQLite, but everything in xCode and advice from the web recommend Core Data. What do you do?
Core Data, xCode and Cocoa provide an ORM (Object Relational Mapping) that assists developers with mapping stored data into Objective C objects. This powerful tool is at once extremely helpful, incredibly seductive and a bit confining. A judicious use of the provided technology allows for rapid development that nears a finished feel for your application. Yet, if you wish to grab the database connection and perform a complex outer join with a group-by - no dice. At least, not with the information held by Core Data.
In researching Core Data, you'll find a lot of advice telling you that "Core Data is not a Database". Although simple to say, that's not quite correct. Core Data, when used with the xCode Data Model Builder and class generation is a (very convenient) alternative implementation to all of the work a developer would otherwise have to do. You are saved from the need to write client/server code, open cursors, perform partial data reads, create Primary Key/Foreign Key relations, create Primary Key sequence generators (select emp_sequence.nextval from dual;), build Intersection (Join) Tables, link up object graphs, etc.
Be sure to build your Data Model using xCode's modeling tool: File > New > Resource > Data Model. DO NOT CREATE PRIMARY KEYS. xCode creates these automatically when modeling relationships between entities (tables). If you really do need a monotonically increasing sequence generator, try this advice from Apple's Mailing List. If you require Primary Key like behavior, read more about uniqueID, a unique handle to an object (row), from Stack Overflow postings here and here.
Inside your program, NSManagedObjectContext provides access to stored data. Use this context to create (insert) objects, fetch (query) objects (full table rows) and properties (columns). The web contains pages of advice for how to use this context. A word of caution - don't make this object a singleton (global variable). Pass it around to the objects that need it. The two most important reasons are (1) the context cannot be used on more than one thread and (2) the context provides rollback capability, so edit screens can be cancelled easily. Read this Stack Overflow Question for some advice.
It turns out that a simple select statement when performed in Core Data requires 13 lines of code! Thanks to Matt Gallagher for this explanation. Look for subsequent posts here that build upon what Matt did.
Matt has also written a great article on the differences between Core Data and a Database. Brent Simmons discusses when it might be appropriate to switch away from Core Data - Spoiler Alert: it rarely is. Derrick compares and contrasts Core Data and SQL. So does Alex in his answer at Stack Overflow.
If you decide you just cannot handle Core Data and want to roll your own solution, there are plenty of middle level APIs to help in your effort. Github holds the source code for FMDB (Flying Meat DB), a SQLite cover, written by Gus Mueller. You could also try Google's SQLite wrapper: Plausible Database.
May your queries by quick and your columns be indexed.