TCMMyClassWithSingleton.sharedInstance

PSA: I switched to dot syntax for Singletons now. Seems fitting, looks less cluttered. I took some time getting used to it, but now I really like it.

[NSUserDefaults.standardUserDefaults boolForKey:@"SomeKey"];
[NSNotificationCenter.defaultCenter postNotificationName…];

Network Testing

If you put in network code into your apps, it is always important to test against bad network conditions. While this isn’t too trivial, it was always possible using dummynet.

However, now new in Lion is a Preference pane to use, so you don’t have any excuses anymore for not testing: Network Link Conditioner - Use it and use it heavily - real life network conditions, especially on iOS, are never as good as in your pristine office big bad ass network location.

Could the Mac become a Post-PC device?

Today I did a little mind experiment thinking about if an ARM based MacBook Air would be viable, and what would be the pros and cons.

Pros:

  • Battery life
  • Return to in house chip development
  • iOS and Mac running on the same architectures
  • Probably a good long term bet, as ARM is here to stay and winning the performance per watt battle

Cons:

  • No Bootcamp, parallels or the such
  • No Rosetta-like intel emulation for existing apps
  • Probably less performant, although with enough Cores going on, probably not that big of an issue.

However the more interesting point to me is that Apple is here to innovate. Mac OS X has kind of seen its time and iOS in itself is a good companion, but not a replacement. If they really want to change the way the standard computer works then a mandatory transition to another hardware platform could be the way.

How could such a future look like:

  • it would be one big step further in the direction Lion is taking. But since backwards compatibility is not an issue there, as developers would have to recompile and adjust to the hardware anyways, Apple could cut with old metaphors and really innovate. E.g. away with the standard file system. Away with the unix shell. Away with the Menubar. Keep technologies and infrastructure, but only expose the Developers to the new environment.
  • redo AppKit from ground up, gone with the slapping on of technologies adapted from the iOS development, a real first class DesktopUIKit, give it the same love that the rewriting did for iOS, perfectly adapted to the new and improved interface metaphors.
  • the platform would be closed in the same way that iOS is closed. Apps only via the App Store, nothing else. There is a clear incentive for Apple to do this, 30% of every sale made.
  • the platform would include full support of iOS and iPad Apps out of the box. E.g. the platform would be comprised of a multiple of the iOS/iPad resolutions so multiple iOS apps could be used at once
  • great integration of multitouch, both via the trackpad and also directly on the screen. 

In essence Apple would create a third platform between iOS and Mac and use it as an opportunity to implement major changes in the user interaction model. It could leverage the existing Developer base in the same way, or even more so than they did with iOS.

The major flaw in that theory: This third platform really needs a good incentive for users to jump on. Something as “magical” as the iPad had. And I have no clue what that might be, but hey, that’s just me, I also thought that Apple needed something special for an iPad to make sense. And all they needed was a great execution of the basic features, reduced to the max to chime in the Post-PC era. The next step: to make the Mac make the jump into the Post-PC era as well.

So these are my 2 cents, here to stay to be laughed at in the future ;)

0x8badf00d, Core Data and Migration

Let’s begin with the basics. In iOS there is a watchdog process - a watchdog that checks if an App takes more than about 20 seconds to start without getting back to the OS it will be killed. In the crash reports this killing is indicated by an exception code of 0x8badf00d - Ate Bad Food. Getting back to the OS more or less means that your delegate call of  application:didFinishLaunchingWithOptions: needs to return control to the runloop in time. If you don’t: you are killed. The user experience of this is that your Default.png is shown for about 20 seconds, and then the iPhone/iPad returns to the home screen.

Fun Fact: On iOS Devices that support multitasking the watchdog can be confused by pressing home and go back to the app again in intervals slightly less than 20 seconds. So if an App is showing this behavior, you might get it to work again using this stunt.

This is all good and well, the user should not have to wait for more than 20 seconds for an App to actually do something that the user can see or interact with. This is where Core Data comes in.

Core Data is a great framework that provides developers an abstraction to a local database. You do so by specifying a data model in Xcode, and provide classes for your database objects. In code you usually initialize your Core Data infrastructure very early on, because you rely on the data in the database for almost everything your app does. Usually that step takes little time - if the app is new you maybe want to copy an existing database as a first step, otherwise it just opens up the database files and returns.

However, if you want to store additional information in your database, you need to update your model. And if you do so, you need to migrate your data once to accommodate the new model. This happens in that early on initialization step. As migration goes there are two kinds of migration: Lightweight migration and manual migration. Lightweight migration is fast and can be done by just issuing SQL statements to the underlying database. Manual migration causes every object to be loaded and be updated. If you can achieve your changes with lightweight migration, especially on iOS devices you should do so. However, sometimes it is necessary to do some manual work in the migration process.

This is where the shit can hit the fan: Consider you are initializing Core Data before the end of  application:didFinishLaunchingWithOptions: because you want to use some of the existing data to prepare badges and UI state in the first viewWillAppear: methods of your top view controllers. Consider further that you are making a major update to your App and the database needs a manual migration. Now what easily can happen is that the watchdog literally bites your app in the ass.

And even better, during development you probably won’t notice. Why? Because of many reasons, I try to list many of them:

  • The watchdog is inactive if you are running in the debugger. Your App Start may take a while, but your app will always start up.
  • The migration step only happens once, unless your specifically testing for it over and over again. And it is most likely that that step is done while you are running in the debugger to check if everything goes right.
  • The data you need to test needs to be big. In most test and development scenarios you have realistic data, with a wide variety of items being in there to catch most cases, but you won’t do very big data because that slows down overall roundtrip time.
  • Your test devices most probably will be the fastest you have most of the time, again for roundtrip reasons. And faster devices migrate faster. Probably fast enough to not show the problem.

When you look at the customers that will have that problem out there you see another problem: They are probably your best customers. If they store a lot of data in your app, they use it a lot. If there is a lot of data, migration takes long. Tada! Horrible.

So now that we have established that this problem is a real turdball coming your way, how do you prevent/fix it?

Solution #1: do lightweight migration. If you can do that. This will be fast enough to not call the wrath of the Watchdog.

Solution #2: if you can’t do lightweight migration, make sure your migration step won’t be caught by the watchdog. E.g. your initialization of Core Data needs to be done after the app has returned from the application:didFinishLaunchingWithOptions: callback. A really good way of testing this is adding a simple sleep(25) in the code that actually creates your persistentStoreCoordinator. That way without having all test data, migration always takes long enough to trigger the watchdog. And again be sure to not attach the debugger, or the watchdog won’t bite.

Lessons learned:

  • Beware of the watchdog.
  • Core Data is great, but migration can really hit you quite unexpectedly.
  • If you use Core Data - put in that sleep(25) call now and test if you get bitten. If so fix it so it won’t bite you unprepared in the future when you update your model.
  • Prepare feedback for user migration. As it turns out users don’t make a difference between an app that appears frozen for a long period of time and an app that actually crashes. Even if migration will only happen once for each major model update, that moment can be crucial.
Didn’t we used to make fun of Eclipse for stuff like this?
Be very careful when calling -[NSNotificationCenter removeObserver:]

rentzsch:

Samuel Défago:

Never ever use -[NSNotificationCenter removeObserver:] to unregister from notification events, except from a dealloc method. This might cancel registrations made by a parent class, and you cannot know how a parent class is implemented (at least you shouldn’t care). Stick to this rule even if your parent class is NSObject: Your class hierarchy might change in the future, and you do not want to run into problems when you don’t have to, do you?

(via Cédric Luthi)

I strongly disagree. There are multiple situation e.g. on the iPhone where registering and deregistering in the [will|did]Appear/Disappear: methods is valid, helpful and good style. (And I took the liberty to fix the classname)

Update: Permature posting is premature. (note to self: always read the whole referenced article). 

So to add some actual wisdom to this subject: Calling the -[NSNotificationCenter removeObserver:] should be confined to situations where you get rid of the object. So I agree dealloc can be the place, but it also can be somewhere up in the chain if you invalidate your object where you already have the knowledge it should go away, but it is not dealloced memory wise. In complex cocoa apps you run in these situations. Also helpful in these cases: [NSObject cancelPreviousPerformRequestsWithTarget: self];

UIEdgeInsets

A common task for UIKit devs: Inset a CGRect by an amount from each edge:

UIEdgeInsetsInsetRect(rect,insets);

And to do that in one line of course the constructor of UIEdgeInsets

UIEdgeInsetsMake(top,left,bottom,right);

Why on earth they decided to make the order of insets the other way round as CSS will remain a mysterie. Also note that a negative inset expands towards this edge. E.g.

UIEdgeInsetsInsetRect(CGRectMake(0,0,100,100), UIEdgeInsetsMake(10,10,-50,50));

results in the following CGRect: {10,10,140,40}

The hidden complexity of iOS 4 Multitasking

Just a Quick list of what to keep in mind when designing your app with the new Fast App Switching in place:

  • the entry point of your app might be anywhere and data may have changed. Even built in Apps like the Photos app get that one wrong. Have the Photos app open. Make new pictures. Go back. Or worse: download and delete all the fotos to iPhoto. Reopen the Photos app: crash.
  • if something goes wrong, the app doesn’t automatically stop and restart when the user presses home. This might seem obvious, but if there are edge cases where you mess up your navigation controller hierachy beyond repair that matters. This happens. The user needs to know how to kill your app in that case, and most don’t. Be extra careful with that.
  • besides the fast app switching way you still need to keep state and position in the app on your own for your next restart. For old devices as well as for when you get killed somewhere along the way. So you still need to do that. Defacto things got more complex instead of easier. However, if you do it right you might also have a good chance of being a good iOS 3.x citizen.
  • if you listen to URLs or Push Notification you need to be prepared for getting them while already running. Worse: you need to detect this yourself using a combination of willEnterForeground and didBecomeActive, so you can handle things differently if you were active and not. Also in the URL case the possiblity exists you get called twice. See http://twitter.com/chockenberry/status/17889156210
  • network connections and hosting die when you get suspended. Be sure to reopen your server if you e.g. provide web access or something similar.
  • your app might not restart for very long time periods, make sure you move important checks and tasks to willEnterForeground or didBecomeActive so they don’t starve out. 

That’s just a quick list of real world issues I encountered. I’m sure there a lots more subtleties and edge cases. Be prepared!

Update:

  • if you have settings you need to make sure that changing those on the fly does work. e.g. the settings change can hit you in any place in your app and you need to be prepared and act like it. e.g. update lists, query the NSUserDefaults without caching, pp
  • if opening an URL/push notification means you jump to another location in your app, the code handing that opening needs to be able to dismiss any - and i mean any - currently showing UI and present itself. This can be a very tough one.

Update 2:

With all the issues involved, opting out by adding UIApplicationExitsOnSuspend with a boolean value of YES to your Info.plist might be the better option if your app has great pre-iOS4 behaviour.

    Separation of Tasks

    If you know me, you might know that I don’t like Xcode’s internal Documentation Viewer and don’t use the internal editor. My reasons: I need my Tasks separated. Source files are in SubEthaEdit, Project files, compilation, console feedback, debugger are in Xcode, Source control management is in the terminal, and Documentation is in AppKiDo (and AppKiDo-for-iPhone). Why? Quick and straight access. I hit the Xcode App Icon for a compile, AppKiDo for Documentation, etc. And they all remember their state. If I do all in Xcode then navigation becomes slow and cumbersome.

    But that all has a side effect: I’m utterly lost in the time AppKiDo needs to catch up with the latest Xcode and documentation release if it breaks anything. Thanks to Andy Lee that is almost always a very very short period of time, but nevertheless makes me anxious all the time. That’s why I really Appreciate a recent contender in the Documentation Viewing business: Ingredients. It looks very nice and modern, but still lacks a few features I use in AppKiDo. Nevertheless: the more the merrier!