iOS Authentication Blocks
In my recent project, Cakewalk, we give the user a fairly complete experience without having to sign up. This seems like the nice thing to do but it’s also in Apple’s guidelines for app approval. At some point, though, we need to create an account for them. In our case, it’s when they want to add an idea to their favorites or save their filter preferences. A “gate” such as this is something I’ve done on the web many times but it’s the first time I’ve done it an iPhone app. I’ll describe how I solved it using blocks.
Web
On the web, I would usually send them to a URL. If that URL requires a logged in user, then it would redirect to the login/signup URL with a query param. For example, if the page was http://www.example.com/favorite
, it would redirect to something like http://www.example.com/login?after_auth=%2Ffavorite
. The login/signup form would add this as a hidden parameter that it POSTs. If the action is successful, it will use this url instead of the default one to send the user to next place. In Rails, it would look something like this:
1 2 3 4 5 6 7 8 9 10 11 |
|
If the site allows the users to switch back and forth between login and signup pages or allows Facebook, Twitter or some other OAuth, it can be easy to lose this parameter. I’ve seen sites put the value in a cookie for this reason. It can simplify it, but is more likely to have unintended site effects.
Blocks
Most of the complexity of the web version is because of the stateless nature of the medium. The query parameter or cookie is an attempt to add that state globally. This is not particularly an issue in an iPhone app. The global state is fairly well known in the view controllers and there is shared memory. The interesting thing that I realized is that it’s not just data that we can store, but also blocks.
An Objective C block is an inline declaration of a function. It is similar to features available in Ruby and Javascript. Here we use one to change how a sort is done:
1 2 3 4 5 6 |
|
I find it a little hard to read when using them like this, so I typedef’d them as needed:
1 2 3 4 5 6 |
|
This allows us to use them throughout the app to provide more semantics to the block in use. For example, in a method declaration:
1 2 3 4 5 6 |
|
Use in context
So most of the time, it does not matter if the user is logged in or not, but then the time comes they hit the “favorite” button and we need to make sure.
1 2 3 4 5 6 7 |
|
This was the cleanest of several alternatives that I came up with. It just occurred to me, though, that I also could have made a block that passed a user instead of nothing for even better encapsulation.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
I did the earlier version because the app already had that shared user concept. Either way, this technique encapsulates all the logic for getting the user authenticated and lets the feature focus on what happens if it is successful (or already logged in).
Callbacks
The system works because the block is passed along much like the web parameter.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
This slides up the authentication controller and most importantly passes along the success callback. Then, if the user does login or signup, the auth controller closes itself, logs the user in, and calls the success
block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
If they do hit the cancel, button, it will slide down and everything will be back where it started. However, if they do login, what I think is really cool about this is that it slides down and the originally invoked code is back in the same position as if the user would have been authenticated in the first place.
Summary
I hadn’t used blocks much in Objective C before, so this ability was a new concept to me. The power of it lies in the simplicity of the favoriteButtonPressed
code even in the face of handling several varying circumstances. I’m sure there are other cases like this so it’s nice to have a new tool in the toolbelt.