Archive for June, 2009

Filtering UITableViews: Deleting Multiple UITableViewCells with Animation

Wednesday, June 24th, 2009

If you have done any iPhone development, you are already intimately familiar with table views. They are an integral part of UIKit. They are highly flexible and customizable.

However, making table views work smoothly with anything but a small, simple, static data set can be trying, to say the least.

Many Cocoa developers have come up with solutions to cope with scrolling performance issues, abstracting cell content, creating preference views, adding network awareness, etc…

Ideally much of this functionality should have been included in the Apple frameworks. Alas, this is not the case. We must use third party solutions to fill the gaps or “roll our own” solutions. Sometimes we need to combine the work of several developers into one class.

Recently, I had to create a network aware table view that also supported filtering with animation. Already having setup a UITableViewController for receiving network data, all that was needed was to filter my data and animate the change. Simple.

Well, not really. If you want to perform that nifty animation that Apple uses when viewing missed calls in the phone app, you will most likely need to make some changes to your current design.

To analyze the problem, lets look at the major components: data model, presentation model, view. We will stay away from code, and look more at the design to accomplish this.

For my typical table views, Matt Gallagher’s solution works fantastic.

CellDeletion1.001.png

The diagram is a bit simplistic, but illustrative (In reality, we have nested arrays representing sections within the table). There are two arrays, one to hold our model data and one to hold our presentation data. Also note, the structure of the data model does NOT necessarily need to reflect that of the table, but it seems logical to organize this particular set of data in this manner.

We have created a “Cell Controller” to manage individual UITableViewCell logic. The UITableViewController now only has the responsibility of creating and destroying these Cell Controllers. This moves the UITableViewController one step closer to adhering to SRP.

One undesirable aspect of this methodology is the requirement to instantiate every Cell Controller and link it to its model data upfront. In return you receive better flexibility and encapsulation without a noticeable performance loss (at least with the moderate sized data sets I have so far worked with).

So, lets look at our basic algorithm for creating our Cell Controllers:

  1. Loop through each dictionary in the model data array.
  2. Create a cell controller.
  3. Add a pointer to the dictionary in the cell controller.
  4. Add the cell controller to the array of cell controllers.

This approach works well for static tables and it is easily adapted for network aware tables. For static tables, you set up your relationships once, and your done. For a networked table, you must add a new object to handle network activity. Then they can communicate through any combination of notifications, KVO, and delegate methods.

CellDeletion2.002.png

This is pretty basic, but it is nice to see the relationships laid out.

Now we need to modify our approach to handle filtering. We need a way to represent the full data set and any subsets. Two methods of accomplishing this task are:

  1. Create separate arrays for each set. One for the full data set and one for the filtered set.
  2. Use a flag to mark filtered results.

Flagging is preferable for a two reasons. In reality, the data didn’t change, we just want to update the presentation. In this case we don’t want to actually modify our data. Additionally, it is more difficult to track several arrays and which one is the “real” data.

As we stated earlier, the presentation model needs changed to display the filtered results. In our previous design, the view controller “looked” at the model and created the cell controllers based on the structure of the data. Not much has changed, but now we just need an additional check. So our algorithm could be:

  1. Loop through each dictionary in the data model.
  2. If a dictionary has the flag set,
    • Create a cell controller,
    • Add a pointer to the dictionary in the cell controller,
    • Add the cell controller to the array of cell controllers.
  3. Finally, replace the old (unfiltered) array of cell controllers with the new (filtered) one.

As we connect the Cell Controllers to their corresponding model data, we check our flag. If we did not want to animate our deleted cells collapsing, we would be finished. We need to devise a method tell our table view which cells have been removed (as well as sections).

There are a few ways to accomplish this. In place of the previous algorithm we could do the following:

  1. In our view controller, loop through each cell controller in the array.
  2. If a cell controller’s model dictionary does not have the flag set,
    • Add the cell controller’s index to an indexPathSet to removed from the array
    • Add the cell’s index path to the array of cell paths to be removed from the table.
  3. Remove the cell controllers from the array of cell controllers.
  4. Remove the table view cells from the UITableView.

Not too bad, but we have completely changed our algorithm. Instead of creating an entire new array of cell controllers, we now remove the ones . The motivation behind this, is that we need to give the table view a list of cells to delete. If we had used our previous algorithm, it would have been difficult to produce this list.

We have just one more issue, but you will not discover it until you try to update a remove all the cells in a section and you app crashes. UITableViews do not like empty sections after deletions. So make sure you remove any empty sections as you remove UITableViewCells.

Hopefully this helps some of you get a head start on your table views. We mostly went over design and algorithms which should help you produce your own code and cut down on much of the trial and error that goes into this problem. In a future post, I will include some code as well.

Let me know if you have already coded your own solution to this or are using open source code.

Clang GUI front-end

Wednesday, June 17th, 2009

If you want to run Clang from a GUI you can using the tool at this site:

http://www.karppinen.fi/analysistool/

Not that the scan-build CLI is that intimidating, but if you have any problems getting reports (I just had an issue with a static library), it may help

Testing, Testing…

Wednesday, June 17th, 2009

Now that the 3G s is on its way out along with a new OS, iPhone developers have some new interesting challenges.

You can be sure that all iPhones will be running OS 3.0, but we now have 5 devices to deal with, each with 5 different sets of capabilities.

If you are like me and bought an original iPhone on day one, you may have some older devices laying around. You may have even received a free iPod touch with a Mac purchase.

The major problem for us now is not whether voice command or a video camera is present, but the speed and memory of each device. We now develop for the sluggish first-gen iPod touch, as well as for the speedy 3G s. That is a huge performance gap.

Without the ability to test on a few of these, you may not be able to predict how you apps will function for your customers.

With 40 M devices out there, you can’t ignore the older hardware. There are still millions of original iPhones and iPod touches in the wild.

How will you cope? Maybe you shouldn’t sell your old iPhone to get a 3G s. Maybe you should search ebay for an extra testing device as others upgrade.

What do you plan on doing to ensure your apps have the highest compatibility?

WWDC thoughts – AT&T

Wednesday, June 10th, 2009

I’m not at WWDC this year, but in light of the announcements, I figured I would chime in on a few things.

I’ll first get my AT&T hatred out of the way. This isn’t exactly developer related, but it is annoying enough that I feel compelled to address it.

I usually have few complaints about AT&T. My reception as been good where I live, customer service has been tolerable, and usually don’t have billing issues. I am not happy with how they gouge for text messaging and internet, but prices aren’t “outrageous”.

But for the first and largest iPhone wireless carrier to be lagging behind the rest of the world is absolutely ridiculous. Their rationale for the MMS delay is like an excuse from a student who didn’t do his homework. The non-answer on tethering is almost better by comparison.

The real reasoning is likely two-fold. First, I am sure they are contemplating how much they can charge us. The other stems from holding a monopoly on iPhone service. They have no motivation to provide good service to retain their iPhone customers. We would never give up our perfect phones and they know it.

Sadly, Apple is also to blame for creating this type of environment. They may have needed to commit to a single carrier in 2 years ago, to obtain certain network guaranties, but those reasons no longer exist.

Just having Verizon, Sprint, or T-Mobile as potential competitors would scare AT&T enough to force better service and competitive prices. From the sound of things, Apple is more than savvy to this fact. I am optimistic that the AT&T’s exclusivity contract will soon be coming to a close.

Getting started with Key-Value Observing

Friday, June 5th, 2009

Getting started in Cocoa can be hard. Just learning the frameworks is a challenge.

Once you get to that point, most or your journey still lies ahead. If you were like me, you probably learned a lot about iPhone programming from online tutorials and books. Then one day, after your 16th tutorial and 3rd book, you looked around and said, “Now what?”

The truth is that technologies like Core Animation, Core Data, and Core Graphics are just tools. They are only a means to an end.

A full toolbox does not make a carpenter good. You still need experience, planning, and vision.

With this in mind, I want to talk about Key-Value Observing. This won’t be a full coding example, per se, but a discussion of how you may use this tool in your code.

Many readers may know the gist of KVO, as well as KVC and bindings. Although bindings are beyond the scope of this post, we will give a quick definition of keys and Key-Value Coding.

KVC is a technology that allows you to access an instance variable by using a string referred to as a key. So using an example:

Interface.png

To access are aptly named dictionary, we can type the two equivalent statements:

kvc.png

They do exactly the same thing, but in KVC we use supply keys using the “valueForKey:” method available to all NSObject subclasses.

To set this property we can use either of the following statements:

kvcgtters.png

This can get more complicated with key paths and some other things. If you would like more information, there are some great tutorials available here and here.

Now to KVO. For those who don’t know, KVO provides a mechanism to receive notifications that an instance variable, has been modified. Of course you know now we will be using keys to do this. First you register as an observer for the key:

setupobserver.png

(Note: I won’t engage in any “self=[super init]” talk here)

We simply registered the class to receive notifications about its own instance variable, but we could have easily selected an ivar of another object.

We also have set some options and a context here, but nothing that need be delved into for our discussion.

To receive the notification, we need to implement one method:

observe.png

Every time the instance variable is changed, this method will be called automagically.

Now that we have a very, very rudimentary understanding of what KVO (and KVC) are, we can begin to talk about how to use them.

One very common use of KVO is model observation. On stack overflow, I see many questions like: “If I have a variable in view controller A, how do I pass it to view controller B?” Of course the real problem is that we are asking the wrong question.

The right question is: “How can I get two view controllers to know about changes of a single variable?”

We know now that we can use KVO. What we want to do is make our views observe our models. By registering view controllers as observers to the model, we can have an unlimited of views showing and/or using the same data. This also gives us well defined MVC boundaries. What’s more, is that we will write almost no code to accomplish this feat.

As you will read elsewhere, KVO has some (few) disadvantages. The one that sticks out, is that it may make your code harder to debug. This is because it is hard to sometimes tell who is observing who. Otherwise this is a very elegant solution that will eliminate some of your glue code.

If you want to go deeper into Key-Value Observing you can read more here, here, here, and of course you can use Apple’s documentation.

What are some uses and patterns of KVO and KVC in your programming?

Cocoa conceptual problems

Monday, June 1st, 2009

After Scott Stevenson put out this question on Twitter and his subsequent blog post, it got me thinking. When I really started digging into Cocoa, I remember staring at UIViewController and UITableViewControler for hours, maybe days. Figuring out what all of those methods did seemed impossible.

In retrospect, I can see these classes represent some of the fundamental design patterns used in Cocoa (touch): delegation, composition, protocols, MVC, nibs, memory management, etc… Once I understood how to use a UITableViewController, I was able to dissect and comprehend the interactions and use of other classes much more easily.

There were many “ah ha” moments when learning those concepts. Previous to iPhone programming, I mainly wrote C and machine level code. It wasn’t my first foray into OOP, but it has been my deepest and most comprehensive.

Learning to write methods that you never actually call yourself was a huge mental hurdle. Splitting class responsibilities according to MVC was logical, but hard to do right (I know, a discussion in itself) the first few times.

Similar to MVC, was memory management. It’s so easy: alloc(or new or copy)+retain = release+autorelease. But when do you actually release? When should you just set to nil? Should you use the accessor or not? Maybe it is best to just use the assign property. Should I create an autorelease pool? Is it best to just “leave the object around” and just handle it if a memory warning pops up. You have a lot of design decisions to make even though the basic premise is simple.

Nibs were simply awesome to layout my views, but learning to hook up the right things and how to use them effectively was no simple task. The outlets and actions themselves weren’t that confusing. The “things you don’t quite have to know until there is a problem” were hard to get my head around. For example: how to load nibs from other nibs, load UINavigationControllers in UITabBarControllers, what the proxy objects actually are, which controller classes to include in the nib, what “really” happens when objects are unarchived, realizing that “initWithCoder:” is called on classes that are defrosted from a nib, how to load UITableViewCells from nibs, etc…  There are many intricacies, most of which you never think about until your app doesn’t work.

After I had some time to think over these problems and Scott’s question, I came to realize (at least for me), Cocoa just took time to learn. Everyone comes from a different background with a different skill set. Add to that, the size and breadth of Cocoa. It should come as no surprise that everyone has to take there knocks when moving to Cocoa and Objective-C, no matter where there coming from (save Next developers).

So, how to wrap this up? To all of the beginner iPhone and Mac developers: know that you have a mountain to climb. Find information where ever you can. The Cocoa development community is great and there is no shortage of resources. Eventually you will reach the “critical mass” of information when you can begin to solve your own problems much more effectively. You will always have more to learn, but will develop an instinct to find what is wrong and begin to ask the right questions.

Is there anything that you think is missing from Scott’s list or that I glossed over? Let me know.