Wrapping Facebook's FBSDKCoreKit with ReactiveCocoa 3.0
I’m currently working on a side project that involves Facebook login, hence the previous post. The Facebook SDK is written in Objective-C and the interoperability with Swift is not that great. Using the API and chaining calls will quickly give you a nice, warm callback soup. Let’s see how we can’t make it (hopefully) better using RAC 3.0.
I’m assuming you already know how to setup a project with ReactiveCocoa and FBSDKCoreKit, using Cocoapods, Carthage or your bare hands.
Requesting a token
The Facebook framework contains a FBSDKLoginManager class you’ll use to request an authorization token with specific permissions. In our case, we care about the “public_profile” one so we can retrieve a user’s id, name and picture. Let’s wrap this call in a SignalProducer
that will send an FBSDKAccessToken.
This code is pretty straightforward as most of the logic is already wrapped by the Facebook SDK, we’re only taking the results and the potential error and sending them down the sink. Of course there is room for improvement: we could catch the error, retry if something goes wrong or the user cancels, etc. but let’s just go straight to the point.
Performing a graph API request
Now that we have a SignalProducer
ready to retrieve an authentication token as soon as it’s subscribed to. That’s not enough though. In a classic approach, we’d add the call to fetch the user profile in the completion block we were just in. This is no classic approach though, what we need is to transform that token in a way that we ultimately end up with the profile informations we’re looking for. To do that, we need another SignalProducer
that will send a request to the Graph API.
You’ll notice that “performGraphRequest” is a standalone function, not a method on our FacebookService
. It could probably be the same for the “requestToken” one, but this code is from an application I’m currently working on, where this class depends on a few other injected services. There are a couple good reasons to use as much standalone functions as possible in your code. Functions are now first class citizens in Swift, so it will help you write code you could easily test and reuse. It also makes it a lot less tempting to add state by relying on values of your class property. In our case, that could be the token we’ve just fetched.
There is also a JSON
object that I haven’t talked about. I mentioned earlier that the interoperability between the Facebook framework written Objective-C and Swift wasn’t great: the callback used in startWithCompletionHandler
is an instance of AnyObject
. Trying to use it directly will lead you straight into Force-Unwrapping-Hell, which is probably not what you want, that’s why I ended up using SwiftyJSON.
At the end of the process, we’re actually looking for a more neutral object, like a simple Account
struct with common informations like identifier, username and picture, for example.
So now we have a function that can fetch a Facebook authentication token, another that can perform a request to the Graph API. We’ll also need to map this JSON object to a value-object conforming to this protocol, for example a FacebookAccount
:
It would be a bit tedious (and not optimal) to use all these methods in our main view controller, for example. That’s why we’ll wrap all this in a method that returns a Signal
we can use to retrieve an account. We don’t really care about the required steps to end up with an Account.
Once we’ve got this method, all we need to do is to subscribe to the SignalProducer returned by the authenticate method, like this:
This is a rough example of how you can use ReactiveCocoa
with the Facebook SDK, hopefully it will make you want to try it in your own app.