Corporation Unknown http://corporationunknown.com/blog Wed, 02 Jan 2013 18:45:52 +0000 en-US hourly 1 http://wordpress.org/?v=3.5.1 UITableViewCell Is Not a Controller, But… http://corporationunknown.com/blog/2013/01/01/uitableviewcell-is-not-a-controller-but/ http://corporationunknown.com/blog/2013/01/01/uitableviewcell-is-not-a-controller-but/#comments Wed, 02 Jan 2013 03:53:42 +0000 paul http://corporationunknown.com/blog/?p=328 Brent Simmons wrote a great defense of his proposed "Cocoa Sin" of "Passing model objects to a UITableViewCell subclass", expanding on the one apparently contentious item in his great list of 20 sins. Go read both posts, he's so completely right.

Except I disagree.

]]>
Brent Simmons wrote a great defense of his proposed “Cocoa Sin” of “Passing model objects to a UITableViewCell subclass”, expanding on the one apparently contentious item in his great list of 20 sins. Go read both posts, he’s so completely right.

Except I disagree. I’ve been down a number of UITableViewCell paths that bit me in the end, many of which Brent seems to have experienced as well, but I have ended up settling on creating UITableViewCell subclasses which take the model object they’re intended to display and break out the properties to the individual subviews so the controller doesn’t have to.

At its core, I’m taking code Brent would write like this:

- (void) updateCell:(MyTableViewCell*)cell 
     forModelObject:(Model*)object 
{
   cell.nameLabel.text = object.name;
   cell.valueLabel.text = object.value;
}

out of the controller and moving it to the cell as

- (void) updateForModelObject:(Model*)object 
{
   self.nameLabel.text = object.name;
   self.valueLabel.text = object.value;
}

(Yes, he defines it as -updateCell:forIndexPath: but -updateCell:forModelObject: can always be refactored out of that once the object is found for the indexPath.)

There may be some additional smarts in there: The color of valueLabel may get changed at a threshold value, the name text may need some truncation or transformation, an icon may change from sun to moon depending on a timestamp in the object. I’ve left them out of the example for simplicity.

At this simplistic level, they look identical. Yet I still feel putting the logic in the cell is superior. Why?

Polymorphism

Look a bit closer at -updateCell:forModelObject:. You may not even have noticed it, but there’s a distinct absence of the keyword self. This isn’t encapsulation; this method is really just a utility method, which could just as easily be written in C: 

void UpdateCellForModelObject( MyTableViewCell* cell, ModelObject* object ); 

Why is this bad? It throws away polymorphism, one of my favorite aspects of object-oriented programming. Let’s start with a theming example: We want to allow the user to chose between a set of differently-styled themes in which to display this homogeneous set of data.

This is fairly simple if it’s limited to colors, fonts, and other style-only changes in the themes—change the style in -tableView:cellForRowAtIndexPath: and continue as before. What if one theme “FirstLast” should display my name as “Paul Goracke” and theme “LastFirst” should display as “Goracke, Paul”? The table view controller needs to know this difference. It’s an implementation detail of the theme, but under this design the controller doesn’t just need to know about it—it needs to implement it.

It will need to implement the quirks of every theme supported. Not bad enough for you? How about this: It also needs to determine which set of theme quirks to use each and every invocation of -updateCell:forModelObject:. Why would you want to do this? I don’t.

By putting the logic in the cell, you only check the theme in -tableView:cellForIndexPath:.

  1. Dequeue or create a cell of the appropriate type MyFirstLastTableViewCell or MyLastFirstTableViewCell.
  2. Call -[cell updateForModelObject:] and the receiver cell will update as appropriate for the single theme it implements.
  3. There is no Step 3.

This is the glory of polymorphism: No matter how many crazy themes I make, the table view controller only needs to know which class to make for the specified theme because the subclass knows its specific implementation. If I decide to sunset a theme, I don’t have to worry about code cruft hanging about in the controller to handle that obsolete theme.

Polymorphism with Heterogeneous Model Objects

A heterogeneous data set in and of itself wouldn’t be a problem as long as all objects provide the core set of properties the cell requires to display the items uniformly. (Such would be the case with Brent’s Twitter app example.) Your cell(s) now take -updateForModelObject:(id<ModelObjectProtocol>), they don’t care which particular protocol implementor you pass them as long as they provide the properties needed to display them, and you move on.

But what of a heteregeneous set where each object type needs to display differently?

Once again, the logic of which cell subclass to create lies with -tableView:cellForRowAtindexPath:. After that, updating the cell’s display of model object property changes is handled by the cell. If the model object at that indexPath were to change type, call -reloadRowsAtIndexPaths:withRowAnimation: on the table view to have it ask for a new or dequeued cell of the type appropriate for the model object’s new type. 

What Makes a Controller a Controller?

Unfortunately, Apple seems to have unintentionally muddied the MVC waters by naming their main controller class UIViewController. Many developers now have an extreme view of a controller: If it touches both a view and model object at all it needs to be a UIViewController. Nothing could be further from the truth. Unless you need lazy view loading and view appear/disappear lifecycle management, creating a UIViewController just overcomplicates matters.

Let’s look at Apple’s schematic of MVC, even though it should already be burned into every Cocoa and iOS developer’s brain:

Model-View-Controller

It’s easy to get caught up in defining these objects based on their positions in the diagram, but what really defines them is their role within the system and how updates propagate. Keep in mind that these arrows are not object references, but interactions via updates and notifications—think of them as IBActions more than IBOutlets.

When a UITableViewCell subclass accepts a model object parameter and updates its constituent subviews as I have described, it is behaving as a data transformer, not a controller. It does not care about any future updates to the model unless the controller tells it to transform the updated model object, or to transform a completely different model instance.

Keys to Success

  • Avoid the temptation to bypass the controller and start key-value observing (notifications are also verboten) on the received model object. This is the hubris that will lead to your MVC downfall (been there, done that). Leave all of the updating logic to the “true” view controller, and your cell subclass will remain a happy and healthy data transformer.
  • Don’t even keep a reference to the received model object. Not only will this help avoid the KVO temptation, but it will encourage you to quickly move the property values into the appropriate subviews and let them take care of drawing, caching and refreshing.
  • You’re a UITableViewCell—you’re displaying only one in a possibly innumerable collection of model objects. Leave the handling of the set of model objects to your UIViewController<UITableViewDataSource> which should be invoking tableView updates based on responding to UIFetchedResultsController or KVO, but there may be other update logic involved. That is, after all, the purpose of a controller.
  • Keep your cells stupid. Brent’s examples of loading image or web service data asynchronously is still something to be avoided in the cell.
  • Keep your cells simple. Many developers try to handle all possible configurations of a cell in one. They have numerous views which are hidden or displayed based on some model object criteria, which leaves a number of unused outlets lying around; this becomes confusing and hard to maintain. Using polymorphic cell subclasses, you can dedicate one cell subclass to each distinct configuration, avoiding unused views and code maintenance overhead.

Conclusion

There is absolutely nothing wrong with Brent’s minimalist guidelines. They will serve you well in many situations. But once your code base gets more complicated, I think you’ll be better served by not fearing better encapsulation of behaviors. Passing a model object to your cell is really only a venial sin.

]]>
http://corporationunknown.com/blog/2013/01/01/uitableviewcell-is-not-a-controller-but/feed/ 3
Define “Success” http://corporationunknown.com/blog/2012/02/20/define-success/ http://corporationunknown.com/blog/2012/02/20/define-success/#comments Tue, 21 Feb 2012 05:47:59 +0000 paul http://corporationunknown.com/blog/?p=310 What comes to mind when I say the word “success”?

Everyone has their own definition: Advancing in one’s career; saving enough to retire; finding a significant other with whom to build a happy, healthy family. Complicating matters further, “success” has much finer-grained contexts: Each successive day of kicking a bad habit; completing a project milestone; passing a test in school. Many “personal improvement” programs would even propose that “success” is only measurable as a continuing accumulation of completed milestones, not any one observable point in time.

So why in the world do you think you can capture “success” in a boolean?

BOOL success = [self doSomething];

That method you just called actually did something, and it did something concrete: It deleted (or saved) a file on disk; parsed some JSON; validated an encrypted string.

Don’t try to capture success in a variable. Name your booleans for what the code actually accomplishes and you’ll find the following logic is much more readable with a decreased likelihood of logic bugs.

]]>
http://corporationunknown.com/blog/2012/02/20/define-success/feed/ 0
Gotcher Address Book! http://corporationunknown.com/blog/2012/02/09/gotcher-address-book/ http://corporationunknown.com/blog/2012/02/09/gotcher-address-book/#comments Thu, 09 Feb 2012 22:25:20 +0000 paul http://corporationunknown.com/blog/?p=302 Even though I installed the Path app and set up an account, I never really used it. It never really clicked with me; I really only signed up because many people I know were trying/using it. So it wasn’t anything close to a “hardship” for me to ask them to delete my account when it was revealed that they were uploading the contents of my address book to their servers. Still, I’m disappointed.

Last fall, we were in the crunch-time week for a client’s app before the iOS 5.0 submission deadline. I was profiling it in Instruments, trying to find where we could improve its launch-time performance, when I found a noticeable amount of time was spent in Address Book queries by a third-party video tracking library.

Not only was this behavior slowing down our launch, it was unexpected and–since it was a binary library without source code provided–we had no idea what it was doing with that information. A red flag was raised, and to my surprise the client even expressed willingness to cut the functionality until the questions could be answered.

As explained, the usage was almost clever: By adding a defined contact to your address book, you would unlock additional debug logging and reporting options for their SDK. They claimed that there were numerous applications already approved with this SDK, and I don’t feel the need to question their veracity. Most importantly, they offered a build without that functionality, provided it quickly, and that’s what shipped.

So it disappoints me that after my own personal experience of trying to keep code out of the Address Book, there are developers out there who apparently don’t think twice about slurping all of your contacts and sending them to their servers without your express permission.

This has happened before with Aurora Feint. It forever tainted my opinion of Open Feint, which came out of that. I had been under the impression that Apple was much more strict about this type of behavior–”chance of rejection” was one of the main reasons I brought attention to the library’s unexpected access. So I’m disappointed in Apple, too.

There have been calls for Apple to add required guards and notifications to the API’s access, similar to how Location Services is handled. That would probably be good, but at a certain point all the notifications just become “Grant Access?” alerts that users don’t think about. I think there’s a simpler way to do it, at least for a first attempt:

  1. When submitting an app to iTunes Connect, ask “Do you access and transmit Address Book data?” similar to the existing question about using encryption.
  2. Answering “yes” is a flag to the review team to verify that the developers have implemented their own reasonable opt-in mechanism, and maybe even a bit more scrutiny of their network traffic.
  3. If an app is found to be violating this, terminate the developer’s account.

Yes, I feel that violating this expectation after making it clear that you are expected to be transparent about using this kind of information is worthy of booting you out of the App Store.

 

]]>
http://corporationunknown.com/blog/2012/02/09/gotcher-address-book/feed/ 0
Farewell, Steve Jobs http://corporationunknown.com/blog/2011/10/05/farewell-steve-jobs/ http://corporationunknown.com/blog/2011/10/05/farewell-steve-jobs/#comments Thu, 06 Oct 2011 01:56:38 +0000 paul http://corporationunknown.com/blog/?p=298 Few depart this world having left a piece of themselves in so many others. I shall cherish my tiny share.

Thank you, Mr. Jobs. Thank you, Jobs family, for sharing him with us.

]]>
http://corporationunknown.com/blog/2011/10/05/farewell-steve-jobs/feed/ 0
How to Fix Patents http://corporationunknown.com/blog/2011/05/18/how-to-fix-patents/ http://corporationunknown.com/blog/2011/05/18/how-to-fix-patents/#comments Wed, 18 May 2011 16:49:43 +0000 paul http://corporationunknown.com/blog/?p=294 As I understand it, the intent of a patent is to protect the value of the invention to the inventor by giving them exclusive control of the rights to use said invention. In return, they are asked to encourage future innovation by sharing that information with the world instead of keeping it a proprietary secret. Pretty simple and straightforward, and I really have a hard time disagreeing with that being a desirable goal.

Most of the complaints about the patent system being “broken” come down to a few points:

  1. The patent is not novel; it’s an obvious method to anyone spending any mental effort on the problem space.
  2. The patent is too broad; It could cover any number of possible techniques without taking a stand on one (or a handful). This usually seems to be done to get around the requirement of sharing the information, keeping aspects proprietary. One might also consider this related to “not novel.”
  3. The inventor (or, usually, reassigned rights holder commonly referred to as “patent troll”) has done nothing with their rights to the patent on their own. On its own, there is nothing wrong with this–it would be perfectly legitimate to have a sole licensee. When combined with items 1 or 2, though, most people agree that it’s is predatory and stifles innovation rather than encouraging it.

My Proposal: Within a defined grace period after patent status is granted (I’m envisioning 5 years, maybe 7), the patent holder must show measurable revenue due to a concrete implementation of the patent, or that it is a recognizable part of another product which generates recognizable revenue. This revenue may be recognized directly by the inventor, or by licensees of the patent. If such evidence is not given within the grace period, the patent is considered fallow and control rights are revoked.

A patent that does not result in a concrete implementation within a reasonable period of time is hard to consider as “benefitting society” and thus unworthy of the rights exclusivity granted by the society in exchange.

]]>
http://corporationunknown.com/blog/2011/05/18/how-to-fix-patents/feed/ 1
It Lives! http://corporationunknown.com/blog/2011/04/24/it-lives/ http://corporationunknown.com/blog/2011/04/24/it-lives/#comments Mon, 25 Apr 2011 00:35:51 +0000 paul http://corporationunknown.com/blog/?p=289 (There is no Easter-related content here; the timing is pure coincidence.)

I just received word that the 2.0 update to ESPN Passport–the last fruits of my contracting with the fine folks of RogueSheep before joining Black Pixel–went live on the App Store today. What started out as “add Facebook Places functionality” became a much smoother, more efficient and focused application that I was proud to have a part in.

If you’re at all interested in adding a social network aspect to your sports watching, go check it out. It’s free, but you’ll need to sign up for a free ESPN Passport login to get the most out of it (Facebook and Twitter logins add even more).

Passport

(By the way, have you tried RogueSheep’s latest app, Easy Alarms? I played no part in it, but it rocks all the same.)

]]>
http://corporationunknown.com/blog/2011/04/24/it-lives/feed/ 0
You Got Your License In My Keychain! http://corporationunknown.com/blog/2011/04/13/you-got-your-license-in-my-keychain/ http://corporationunknown.com/blog/2011/04/13/you-got-your-license-in-my-keychain/#comments Wed, 13 Apr 2011 16:18:21 +0000 paul http://corporationunknown.com/blog/?p=278 The One In Which Paul Talks Out His Hat (metaphorically, since he and hats don’t get along).

During an entertaining aside in Andy Ihnatko‘s keynote at this weekend’s Voices That Matter conference, I was reminded of a thought I’d had a while ago: Why don’t Mac desktop applications store their license info in the Keychain?

Complaining about restoring license keys should be just as trite of a humor topic as little bags of peanuts on flights, but it still holds true. Go to restore a machine, or move to a new one with a clean install, and you can write off at least an afternoon launching and entering at least two text fields per application.

Maybe I’m the only one who feels quite comfortable with syncing to MobileMe–I actually consider it the feature that keeps me coming back. (I do know there are people who have frustrating issues, and I truly feel for them, but I continue to believe they are in the minority.) Given that setup, here’s how I’d see setting up a new laptop:

  1. Configure my MobileMe account and sync down the data, setting up my Mail accounts, Address Book and Calendar–and Keychain.
  2. Launch a new copy of my licensed application.
  3. There is no Step Three. Go on your merry way.

I’m not expert enough to suggest that it would improve security of your key, but it sure would discourage casual tweaking more than the plist or hidden file schemes you’d need to develop on your own. Also, separation of license key from a preferences file (hopefully developers are already doing this) allows a user to delete the preferences and start fresh without having to worry about relicensing.

I understand that Keychain development can be convoluted, but is that reason enough to not make the user’s life a little bit easier and your support burden of finding a lost license a little less frequent?

As I said at the top, I realize I’m talking out of my…hat here. I don’t have experience doing this (yet), so I am interested in hearing reasoned arguments. Until and unless this is proven untenable, simply consider it a feature request for your next release.

]]>
http://corporationunknown.com/blog/2011/04/13/you-got-your-license-in-my-keychain/feed/ 7
iPad and the Digital Hub, Revisited http://corporationunknown.com/blog/2011/03/10/ipad-and-the-digital-hub-revisited/ http://corporationunknown.com/blog/2011/03/10/ipad-and-the-digital-hub-revisited/#comments Thu, 10 Mar 2011 14:33:02 +0000 paul http://corporationunknown.com/blog/?p=274 Apple released iOS 4.3 yesterday, and it looks like iTunes Home Sharing provides almost everything I had wished for in my original post.

There seem to be some quirks (all videos and podcasts are marked as unplayed on the device) and other behaviors I’d like changed (watching/listening to a podcast on the device doesn’t report that to the shared library). These niggles are really just small issues compared to the big advance of having access to my full library.

Thank you, iOS team!

]]>
http://corporationunknown.com/blog/2011/03/10/ipad-and-the-digital-hub-revisited/feed/ 0
Year in Review 2010 http://corporationunknown.com/blog/2011/01/11/year-in-review-2010/ http://corporationunknown.com/blog/2011/01/11/year-in-review-2010/#comments Tue, 11 Jan 2011 16:19:29 +0000 paul http://corporationunknown.com/blog/?p=261 It’s still the first half of January, so I’m in the grace period for a “Year in Review” post.

2010 was one heck of a slingshot year for me and Corporation Unknown. I started the year unemployed and thin on contract work, and finished it with Corporation Unknown’s highest revenue in its 3-year history and a job offer I can’t refuse.


Bankruptcy

After starting out looking promising, 2009 was rough. The latter half was my first try at full-time independent contracting. I spent time working on project proposals where the client apparently went with someone willing to work for $25/hour; I did work on a decent project (iPhone client for the web site they were developing) that then went “on hold” once the company I was subcontracting for realized that their client was behind on payments to the tune of six digits. (They paid me in full, I don’t know if they ever got the same.)

At the beginning of 2010, my wife and I filed for personal bankruptcy. This isn’t something I’m proud of, but I refuse to hide it as a personal shame. I debated even mentioning this here, except for two points I hope might be able to help others approaching a similar situation:

  1. Start looking into bankruptcy before dipping into retirement savings. Most likely, those funds are protected from the proceedings and you won’t have to start over.
  2. The idea of paying a lawyer $3k to file for bankruptcy seems wrong, doesn’t it? It was completely worth it. Under our attorney’s advice, we knew exactly what to expect with no surprises. Sometimes when trying to restart parts of your life, it’s refreshing to just chuck it all and walk away, consequences be damned–this is not one of those times.

Contracting

Independent contracting in 2010 was hot and cold. More accurately, cold then hot, and feast or famine. I’d go without prospects for a while, then have a few come all at once that I actually had to choose from. Managing a client pipeline is not easy as a solo contractor.

In the spring, I had the opportunity to work with local friends Black Pixel on a small part of a fairly large project for iPad–before its release. It’s hard to believe the iPad hasn’t been out a full year yet; recalling having to code for iPad without a test device feels like reminiscing about the punchcard days.

I had a good time working on that project, even though it was all too brief. The team was wonderful, and even though it feels unfair, I have to single out the experience of working with Chris Clark. It was the first time I’d had the opportunity to work on an iOS project with a dedicated designer–one who listens, takes feedback, and respectfully tells you you’re either wrong, or he just prefers his design anyway–and I loved that.

At that time, I was also working on a project with a existing client. They had an iPhone app that I had cleaned up somewhat, and the announcement of iPad seemed an ideal fit for their product. Unfortunately, in the early days of the iPad, I still needed a lot of experimenting to get a feel for what “worked” and what didn’t. In their desire to get in to the field as quick as possible, they felt that the project was taking too long and ended up opting out of the contract.

Then in May, I had the opportunity to do some subcontracting work for fellow locals RogueSheep. The project was for a component of an iPad app that, once I got past the “why would you want to do that?” aspect, provided some interesting challenges in designing custom controls and animations, and I loved that.

RogueSheep is another great team, and another great designer in Brad Ellis (even if he has since moved on to Square). Brad has the same positive qualities as Chris, with the addition of slightly crazier ideas that push you to create something you wouldn’t have imagined on your own.

Thankfully, the Sheep have continued to have a project pipeline for me to work on: I wrote another bit of code that I’m not even sure has made it onto the store yet for the same client; I wrote some UI code for RogueSheep’s own TouchUp for iPad application as they were readying it for release, and I’m in the process of finishing off another pretty major iPhone project for another of their clients. They even let me touch their Apple Design Award.

Thanks in large part to RogueSheep, in its first full year of full-time effort, Corporation Unknown has recorded its highest annual revenue:

CU Revenue.png

Onward

This year already has some promising challenges ahead:

  1. Beginning in March, I will be teaching “Developing with the iPhone SDK,” the middle course of the iPhone and Cocoa Development certification from the University of Washington’s Professional and Continuing Education.
  2. I have accepted an offer to return to Black Pixel full-time beginning in February. It’s been tough feeling like I’m choosing between two great companies and teams, but I am really excited to be rejoining Black Pixel’s now-expanded team and continuing to kick some iOS butt.
  3. On top of all that, our second child is due at the end of July.

2011 is going to be a busy year!

]]>
http://corporationunknown.com/blog/2011/01/11/year-in-review-2010/feed/ 1
Bughunting a Bashful Table View http://corporationunknown.com/blog/2010/12/14/bughunting-a-bashful-table-view/ http://corporationunknown.com/blog/2010/12/14/bughunting-a-bashful-table-view/#comments Tue, 14 Dec 2010 15:46:15 +0000 paul http://corporationunknown.com/blog/?p=250 I ran down a strange bug yesterday that I thought I would recount in the hopes of saving someone else the half day of frustration.

I was going along, minding my own business, implementing a typical -tableView:didSelectRowAtIndexPath: delegate method to create a view controller, push it onto the navigation stack, and then…nothing. The view didn’t appear, and the app interface became unresponsive. Numerous pauses in the debugger showed what would appear to be normal stacks in the running of the app–no infinite recursion going on, thankfully.

The view was actually in a strange nesting of UITableViewControllers, UINavigationControllers and UITabBarControllers, so I reworked that to the bare bones of a UITableViewController pushing a freshly made UITableViewController onto its navigationController. I verified that it was being initialized properly, and that I had a valid navigationController to push onto (and other view controllers did push with the same navigationController). Still no change.

Then I added breakpoints to every UITableViewDelegate and DataSource method I’d implemented, even the trivial hardcoded ones (“return 3;” for -numberOfSectionsInTableView:) at first to ensure they were being called (they were). Thus began the tedium of following the chain of 4 or 5 calls per table row until I found the offending code.

This new table view is composed of a number of sections each composed of only one cell. The cells are designed in Interface Builder as top-level objects, and are IBOutlets of the File’s Owner View Controller. Technically, it’s one big scroll view but I’m using this design to provide some layout flexibility if the client wants to reorganize sections, and I thought it would be cool to have the dynamic scrolling of section headers, too.

Since I want the IB objects to define the layout, I determine the height of each row based on the view’s frame:

	height = CGRectGetHeight( self.playerStatsCell.frame );

The value for height of this cell (the last on the list–the others were fine) was a “whopping” 6.30104785e-38. What was going on here?!

Well, self.playerStatsCell wasn’t actually connected yet because I was testing (trying to test) one row/section at a time. Calling the frame method on a nil object seems like a normal thing to do in messaging-nil-happy Objective-C. But it’s not. The call is effectively short-circuited, and the temporary CGRect variable the return value was supposed to fill is left uninitialized, which is the bizarre height value returned by CGRectGetHeight.

This call is equivalent to:

	CGRect frame; // uninitialized struct
	frame = self.playerStatsCell.frame;
	height = CGRectGetHeight( frame );

A simple change to the following would fix it:

	CGRect frame = CGRectZero; // initialized struct
	frame = self.playerStatsCell.frame;
	height = CGRectGetHeight( frame );

(In my case, I resolved it by connecting a temporary cell object in IB.)

What did I learn from this?

  1. Messaging to nil is normally fine, but definitely not for struct-returning methods.
  2. Returning a cell height that is very, very tiny from -tableView:heightForRowAtIndexPath: results in “strange behavior.”

I still don’t know exactly what the app was doing while it wasn’t displaying the new table view, but now I know what to quickly look for when I can’t push a UITableViewController in the future.

]]>
http://corporationunknown.com/blog/2010/12/14/bughunting-a-bashful-table-view/feed/ 0