(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.
Sincerely,
- 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.
Saturday, April 7, 2012
Sunday, April 1, 2012
Core Data Primer for SQL Hackers
"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.
Saturday, March 31, 2012
Adding IVars to Library Classes in Objective C
For a recent project, I needed to add some data to one of the Core Data classes (NSManagedObjectContext) and found Tom Harrington's post regarding a bug work-around he implemented, which required adding a method for freeing a memory block upon class dealloc (a class he didn't control). He details how he creates a Category for NSObject and a separate class to hold an array of deallocators.
I tweaked Tom's approach slightly, to get the following design methodology: Adding IVars to a Class (and it's subclasses). A future post will explain in detail why I needed these extra parameters, but briefly, I want to extend NSManagedObjectContext, a class I do not own, with methods that short cut the work required to build and execute fetching from Core Data. Adding a little bit of state and/or default values to the context (instead of passing that data into/out of the method calls) simplifies matters. Making IVar access transparent eases the burden on the caller. Since I don't own the class definition, I needed another method to associate data and Tom's post pointed the way.
Example Code
CompactFetch.h - Category declaration
In our header, we create a category, CompactFetch, for NSManagedContext. Notice the two properties created. These property declarations do not use @synthesize for their implementations. The nice thing about making them properties, is that code can use dot-notation to set or get their values. In addition, the code is KVC compliant. Note, you can use objects here, just be careful with assign & retain.
CompactFetch.m - IVar class declaration
I tweaked Tom's approach slightly, to get the following design methodology: Adding IVars to a Class (and it's subclasses). A future post will explain in detail why I needed these extra parameters, but briefly, I want to extend NSManagedObjectContext, a class I do not own, with methods that short cut the work required to build and execute fetching from Core Data. Adding a little bit of state and/or default values to the context (instead of passing that data into/out of the method calls) simplifies matters. Making IVar access transparent eases the burden on the caller. Since I don't own the class definition, I needed another method to associate data and Tom's post pointed the way.
Example Code
CompactFetch.h - Category declaration
@interface NSManagedObjectContext (CompactFetch)
@property (assign,nonatomic) NSUInteger defaultFetchLimit;
@property (retain,nonatomic,readonly) NSError* lastError;
@end
@property (assign,nonatomic) NSUInteger defaultFetchLimit;
@property (retain,nonatomic,readonly) NSError* lastError;
@end
In our header, we create a category, CompactFetch, for NSManagedContext. Notice the two properties created. These property declarations do not use @synthesize for their implementations. The nice thing about making them properties, is that code can use dot-notation to set or get their values. In addition, the code is KVC compliant. Note, you can use objects here, just be careful with assign & retain.
CompactFetch.m - IVar class declaration
@interface CompactFetchIVars : NSObject
@property (assign,nonatomic) NSUInteger defaultFetchLimit;
@property (retain,nonatomic) NSError* lastError;
+ (CompactFetchIVars*)fetch:(NSManagedObjectContext*)moc;
@end
@property (assign,nonatomic) NSUInteger defaultFetchLimit;
@property (retain,nonatomic) NSError* lastError;
+ (CompactFetchIVars*)fetch:(NSManagedObjectContext*)moc;
@end
Next, we create a class, CompactFetchIVars, which mirrors the @property declarations on the Category. Again, be sure to match up the assign & retain annotations. Although the property names need not be the same, keeping them that way reduces confusion.
Look at the class method, +fetch:, the heart of our design. It takes an NSManagedObjectContext and returns an instance of our class. We use this class method to attach new ivars (new data) onto the Core Data class. By the way, this @interface resides in the .m file, there's no reason to expose the caller to this detail.
Now, for CompactFetchIVars's class definition:
CompactFetch.m - IVar class definition
@implementation CompactFetchIVars
@synthesize defaultFetchLimit, lastError;
+ (CompactFetchIVars*)fetch:(NSManagedObjectContext*)moc
{
static void *compactFetchIVarKey = &compactFetchIVarKey;
CompactFetchIVars *ivars = objc_getAssociatedObject(moc, &compactFetchIVarKey);
if (ivars == nil) {
ivars = [[CompactFetchIVars alloc] init];
objc_setAssociatedObject(moc, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[ivars release];
}
return ivars;
}
- (id)init
{
self = [super init];
return self;
}
- (void)dealloc
{
self.lastError = nil;
[super dealloc];
}
@end
@synthesize defaultFetchLimit, lastError;
+ (CompactFetchIVars*)fetch:(NSManagedObjectContext*)moc
{
static void *compactFetchIVarKey = &compactFetchIVarKey;
CompactFetchIVars *ivars = objc_getAssociatedObject(moc, &compactFetchIVarKey);
if (ivars == nil) {
ivars = [[CompactFetchIVars alloc] init];
objc_setAssociatedObject(moc, &compactFetchIVarKey, ivars, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
[ivars release];
}
return ivars;
}
- (id)init
{
self = [super init];
return self;
}
- (void)dealloc
{
self.lastError = nil;
[super dealloc];
}
@end
A typical class definition, we have @synthesize and init/dealloc. Note, -dealloc silently releases lastError through the nil assignment (via the synthesized property).
What's +fetch: up to? objc_getAssociatedObject retrieves an object previously associated with the target class, our NSManagedObjectContext. If this is the first time through, there is no association and nil is returned. In that case, we must create and associate an instance of our class, CompactFetchIVars, with the context. objc_setAssociatedObject performs the association. Note that both the get & set association calls require a key (in this case, compactFetchIVarKey), and despite the documentation, that key need not be a string. The functions use the key's unique location in program memory (assigned by the compiler) to determine uniqueness of the key.
CompactFetch.m - Category definition
@implementation NSManagedObjectContext (CompactFetch)
- (NSUInteger)defaultFetchLimit
{
return [CompactFetchIVars fetch:self].defaultFetchLimit;
}
- (void)setDefaultFetchLimit:(NSUInteger)limit
{
[CompactFetchIVars fetch:self].defaultFetchLimit = limit;
}
- (NSError*)lastError
{
return [CompactFetchIVars fetch:self].lastError;
}
- (void)setLastError:(NSError*)error
{
[CompactFetchIVars fetch:self].lastError = error;
}
//...
- (NSUInteger)defaultFetchLimit
{
return [CompactFetchIVars fetch:self].defaultFetchLimit;
}
- (void)setDefaultFetchLimit:(NSUInteger)limit
{
[CompactFetchIVars fetch:self].defaultFetchLimit = limit;
}
- (NSError*)lastError
{
return [CompactFetchIVars fetch:self].lastError;
}
- (void)setLastError:(NSError*)error
{
[CompactFetchIVars fetch:self].lastError = error;
}
//...
Finally, we implement the get/set methods for the Category we attached to NSManagedObjectContext. In them, we use the +fetch: class method to retrieve (or create, associate and retrieve) the instance of CompactFetchIVars attached to the NSManagedObjectContext. Then, we get or set the parameter, as appropriate. Under the covers, this goes from a method call on the Core Data instance via the Category through to the attached IVar instance. The runtime knows to call -dealloc on both NSManagedObjectContext and CompactFetchIVars, when retain count goes to zero. DO NOT RETAIN a reference from the IVar class to the attached class; any back track should be assign only, or a retain cycle is created and memory leaks.
Friday, March 16, 2012
Trip Report: Salesforce.com's Cloudstock
Cloudstock
On March 15th, I attended Salesforce.com's Cloudstock (& Cloudforce).
First off, Marc Benioff has built an incredible company - for the industry, for their customers and for their employees. I know at least a handle of current and previous employees that have nothing but good things to say about their employer.
As for the event, if you are even remotely involved in High Tech, I would encourage you to attend. They did a 2.5 hour keynote in the morning that featured C-level employees, customers and, of course, Marc. Marc is inspiring and paints a great picture of where we are in the technological evolution of computing (mainframe, PC, client-server, web, mobile, social). It's not an earth shattering story (if you've been around awhile, you know it), but what is apparent is Salesforce's ability to pivot and take advantage of the last three.
Confession: I left the keynote after 90 minutes, that was enough for me.
Mid-day, the expo floor opened. There were two main sections: Cloudstock and Cloudforce. The former was for developers, so I headed over there. I never did make it to Cloudforce.
One thing that stood out, among many stand out items, was the high quality conversations I had with every person I met at the show.
The Expo
DataSift.com - Far and away, the most impressive product was from DataSift.com. Imagine your company wants to see every social media tweet, post, facebook, yelp, digg, etc. about itself. Furthermore, the data should be tagged and categorized by geo location, sentiment, and whatever else the marketing research department wants. You could build a feed reader for the 30 or so social media platforms and collect the portion of the data they send you (you won't get everything you want). Or, you could go through Data Sift's interface, build a simple query (via GUI or by hand), and have your data feed running in less than a minute. Output is in JSON and easily handled by current tools. Definitely, check them out.
Heroku - Salesforce bought Heroku, recently. Heroku is a cloud platform development platform whose backend runs in AWS. If you like to code in their provided languages (Java, Ruby, PHP), you can use their system "out of the box". If not, you can provide your own libraries for install. They also integrate with Database.com (another Salesforce acquisition). They have a dyno model, in which your system runs. They claimed this provides some higher levels of security. Securing cloud services against back-end attacks should be the chief security concern for every cloud application. I'm skeptical of the level of protection, however, they said a security white paper was forthcoming. Stay tuned.
StackMob - Another backend system, this one dedicated to running your server side operations without having to do any infrastructure work. Basically, think of having to write only the Java classes that respond to GET/POST calls for an Apache Tomcat plug-in with some nice glue into a SQL database. That's StackMob.
Database.com - I didn't speak with these folks at great length, but they have a nice product. Check them out.
Force.com - This is Salesforce's cloud development platform. They've pivoted hard into the social part of the web, leveraging their Chatter application and creating a social media datastream for the Enterprise. There's some great stuff in there that I won't review, you can check out their site. There are, however, some items worthy to note, they have two programming models for handling mobile: an SDK and a hybrid wrapper. The SDK provides calls into the force.com system and is to be integrated into a native application (iOS/Objective-C). The hybrid wrapper uses PhoneGap (below) to wrap HTML/JavaScript.
PhoneGap - These folks provide a shell that wraps HTML and JavaScript web pages. They have developed a JS library to provide native access to phone hardware (camera, GPS, etc.). If you like, you can even build your own native operations and map them to JS calls compiled into the wrapper. The wrapped code gets submitted to the application store. Although I'm a big fan of the mobile web, some applications do best using the mobile platform's look and feel and will continue to be coded in iOS or Java. I think if you're in a hurry, this is a good way to go.
Sencha - These folks have a back end web server that can build for the mobile web browser (HTML5/CSS) or for Desktop browser. I wasn't clear if the browser required a plug-in, but the mobile web pages they showed me looked quite clean. Worth checking them out if you want standard, mobile web browsing.
In the notable mention side, I'd like to put a plug in for Brain Engine and GitHub.
Brain Engine provides an augmented IDE that runs in the cloud and sits atop the Force.com infrastructure. As I have not done a lot of Force.com development, I couldn't tell you if they truly benefit the user, but they seemed convinced and seemed to know what they were talking about. In addition, we discussed a downfall of the Force.com platform, something they are not able to fix, either.
Apparently, Force.com doesn't have a good (or any) source control. All programs are checked into the DB, and there's no branching or merging. That has to be a nightmare for development. Salesforce should address this problem and their users should demand a change.
GitHub - They provide hosted source control and, also, an issue tracker. There are plenty of cloud based source control systems out there. My friend uses GitHub and is quite happy. YMMV.
Finally, an SalesForce architect and I were discussing securing at rest data on the phone and may have found what could a huge security misunderstanding for mobile development work. We are researching the problem and will write a paper, if true. Be on the lookout.
Monday, February 27, 2012
Objective C Explanation
I found the following post at Cocoa Dev an excellent introduction to the Objective-C programming system. It has cleared up quite a bit of confusion regarding accessors, object instantiation and properties.
http://cocoadevcentral.com/d/learn_objectivec/
I now need to find a good explanation of the memory management model (when and how to use garbage collection vs. code level memory mgmt).
http://cocoadevcentral.com/d/learn_objectivec/
I now need to find a good explanation of the memory management model (when and how to use garbage collection vs. code level memory mgmt).
Sunday, December 11, 2011
Audio Delay
I've always wanted to build an audio delay box, something that would take an audio input (mono or stereo) and delay it by some variable amount (user determined). Why? Because when I've watched the Saturday afternoon game and the baseball playoffs for my home team (SF Giants), I realize just how lousy those national league announcers are. But, playing an out of sync radio broadcast with the TV broadcast is the stuff of nightmare. The radio is always ahead of the TV broadcast because the radio is transmitted locally and the TV broadcast goes around and around, up and down, cable, satellite, whatever.
So, I need an audio delay device. The Arduino family seems like a great place to start building such a device. Just before starting to build this project, I looked around to see if anyone had built one of these. There are plenty of options for products between $60 and $200 that delay between 1 second and 1 minute. I found one product that could delay up to 20 minutes.
I only need a delay of max 5 seconds, but I'm an engineer, so as big as I can make it will make me happy. Plus, I need to build it for less than $60 in parts, or I might as well buy it. Actually, that's not exactly true, any cost over $60 pays for my enjoyment in building the project, but you get what I mean.
If I use the Arduino (or the Mega) that's half or all of my budget, so this engineering process should get interesting.
I have a few ideas for designs and will make follow up posts detailing them with some pros and cons. In short, here's what I've thought through so far:
So, I need an audio delay device. The Arduino family seems like a great place to start building such a device. Just before starting to build this project, I looked around to see if anyone had built one of these. There are plenty of options for products between $60 and $200 that delay between 1 second and 1 minute. I found one product that could delay up to 20 minutes.
I only need a delay of max 5 seconds, but I'm an engineer, so as big as I can make it will make me happy. Plus, I need to build it for less than $60 in parts, or I might as well buy it. Actually, that's not exactly true, any cost over $60 pays for my enjoyment in building the project, but you get what I mean.
If I use the Arduino (or the Mega) that's half or all of my budget, so this engineering process should get interesting.
I have a few ideas for designs and will make follow up posts detailing them with some pros and cons. In short, here's what I've thought through so far:
- ADC -> MEM -> DAC + CPLD/FPGA + Arduino
- ADC -> Flash Mem -> DAC + CPLD/FPGA/Arduino
- ADC -> MEM -> DAC + Arduino Mega (which has lots of address pins)
- Delta-Sigma Modulator + Delay Queue (implemented w/ Arduino + majolsurf mem expansion)
For MEM, I was thinking of an old DDR2 board.
Basically, the designs come down to some combination of the following elements:
- An Analog to Digital Converter that outputs one or two channel (mono v. stereo) and 1 bit or N bit (generally, 16 bits). However, even the N-bit ADCs really stack up 1 bit outputs and then release them as N bits, but on a serial line! Therefore, the N-bit ADCs are simply bursty one bit ADCs. None of them have 8 or 16 parallel output, so their output needs to be thrown into a shift register if to be used in parallel.
- A Digital to Analog Converter (1 bit or N bit). If data held in parallel, then it needs to be placed in an SR and sent to DAC, serially. N-bit DACs don't accept input in parallel.
- Memory
- Flash memory accepts input as a bit stream and it should be in page chunks, that is, written or read sequentially. Flash memory really isn't meant to be Random Access, or least NAND based flash isn't.
- DDR2 has some interesting requirements (a section of low addressed memory that is off limits for storage) and 200 PINs to drive or read. The Arduino Mega could run a DDR2 board.
- Data Transfer Protocols
- I2C or SPI or Parallel or NIH (Not Invented Here or Roll My Own).
- The challenge of the first two are lack of familiarity and/or speed limitations.
- The challenge of the second is a heck of a lot of wiring - ugh!
- The challenge of the third is inventing a stable (serial) protocol better than I2C or SPI.
- Delay Control - one or two knobs that allow the user to dial in a delay (coarse/fine grained delay). It would be great if the delay could be from sub microsecond up to limit of the system. Also, if the system delay maximum is large, the coarse grained control should be non-linear in response (smaller steps closer to zero).
- Display Delay - it would be nice if the user could see how much of a delay the system was using.
- Controller/Control Signaling - somehow, all of these elements need to communicate with each other and do the delay work.
In the end, the system converts analog voltage levels into frequency bit streams that get delayed then returned back into a voltage signal. Everything else is just implementation.
Sunday, December 4, 2011
Hardware Hacking
There are some fabulous hardware devices out there for hardware hacking - it warms my EE heart. Having been out of the field for decades (really, only played with this stuff in college and high school), I'm enjoying getting back into things.
That said, folks who've never studied the material before can still play.
First, get yourself and arduino or similar device.
Then, learn some basic electronics - youtube is a great place to start.
Then, build and enjoy...
That said, folks who've never studied the material before can still play.
First, get yourself and arduino or similar device.
Then, learn some basic electronics - youtube is a great place to start.
Then, build and enjoy...
Subscribe to:
Posts (Atom)