Facebook (Un)Likes Developers

You are here

Facebook gives developers the permanent thumbs down. Sad but true. Awful docs, worse support, no human to talk to. Every time I do a Facebook app, be it iOS, web, Android, whatever, I come away with the same impression: we're Facebook, you, little ant, can piss off.

Let's start with the basics: documentation. You would think the Quickstart guide for their Javascript API would may contain code that actually ran. I mean, is that too much to ask? Those 10-15 lines of code to do the basic "Hello World" of Facebook should actually run, right? Maybe even the code to just IMPORT THE LIBRARY would work, right?

Nope.

Try it for yourself, Here's the code from Facebook's Javascript Quickstart:


      window.fbAsyncInit = function() {
        FB.init({
          appId      : 'your-app-id',
          xfbml      : true,
          version    : 'v2.1'
        });
      };

      (function(d, s, id){
         var js, fjs = d.getElementsByTagName(s)[0];
         if (d.getElementById(id)) {return;}
         js = d.createElement(s); js.id = id;
         js.src = "//connect.facebook.net/en_US/sdk.js";
         fjs.parentNode.insertBefore(js, fjs);
       }(document, 'script', 'facebook-jssdk'));

Of course, you're supposed to replace "your-app-id" with your actual app ID from Facebook. But do that, then run this code. What do you get? You get "FB is undefined" in the fbAsyncInit callback. That's what you get. It doesn't run on the latest flavor of Chrome, it doesn't run on Safari, it doesn't run on Firefox. It just doesn't run.

And that's just the first few lines in the Quickstart! You would hope Facebook would actually test what they put in their docs, right? Guess not.

But what about that wacky code itself, what the heck is going on there? Let's start with the second bit that starts with:

(function(d, s, is){

That's the mostly well-known module pattern in Javascript. Essentially, it is an anonymous function that runs itself at load time. You would do this to isolate your scope from the rest of the Javascript and initialize. Fair enough, it's a great pattern, why not use it? But now, let's not document it, or use particularly useful variable names, huh? Sounds good to me. Not.

All this overly complex, undocumented piece of crap does, is add this to your HTML:

<script src="//connect.facebook.net/en_US/sdk.js" id="facebook-jssdk"></script>

Pretty cool, huh? Some intern decided to show off how much javascript he knew by writing the useless junk above. Thanks for that, I'll just include the library the old fashioned (working) way, 'K? And as far as I can tell, you don't need the "id" bit polluting your CSS namespace either. Leave it out.

OK, so what about that super-cool callback when the library is done loading; the fbAsyncInit method patched onto the window object? Well, it doesn't work! Shocker!! FB is not defined by the time this gets called, so calling FB.init() will just crash.

The fix is, once again, to ignore the Facebook docs and do it yourself. It turns out, if you simply poll to see that the FB object is defined before you use it, then take a little nap if it isn't (setTimeout for 100ms, say), FB does end up getting defined. Undoubtedly, the Facebook Javascript code is running a module pattern that initializes itself, but their proscribed callback doesn't work, so do it like so:


function checkFB(){
    if (FB!=undefined){
        // call your code where you use the FB object. Calls like FB.init(), FB.login() will run now.
    } else {
        console.log("FB still undefined, checking again in 250ms");
        setTimeout(checkFB, 250);
   }
}

That's just as an example folks, I haven't used that exact code (and the above isn't usable as is). And by the way, it seems it never takes more than a single timeout cycle to init.

Now here's what I really use. I'm a huge AngularJS fan, so I am wrapping up my FB stuff in an Angular service. Here's a simple example:


angular.module('ngFbApp', ['ui.bootstrap', 'ngSanitize'])

.factory('ngFB', function($timeout, $rootScope){

        var svc = {};
        var fbReady = false;

        function checkFB(){
            if (FB==undefined){
                fbReady = false;
                console.log("ngFB: FB is undefined");
                $timeout(checkFB, 250);

            } else {
                fbReady = true;
                console.log("ngFB: FB is defined");
                $rootScope.$broadcast('ngFBReady',{});
            }
        }

        svc.fbReady = function(){
            checkFB();
            return fbReady;
        }

        return svc;

})

.controller('fbAppController', function($scope, ngFB, $timeout) {

        $scope.$on('ngFBReady', function(e, d){
            console.log("FB defined, running init.");
            FB.init({
		      appId: 'your-app-id',
		      xfbml: true,
		      version: 'v2.1'
	      });
        })

     // Trigger the polling loop in the service
      ngFB.fbReady();
     ...

Even if you're not an AngularJS user (you should be, hint hint), you can probably figure out what's going on there. The service is initialized when it is injected into the controller (see ngFB in the arguments for the controller call?). The call ngFB.fbReady() just kicks off that poll loop in the service where we're waiting for FB to become defined, then I broadcast a message to modules that need FB telling them it is safe to use. No patching "window", no nutty DOM modifying imports. This just works.

In future posts, I'll show you how to do a fairly simple Facebook photo post application, all in AngularJS.

And I won't be using any code from the Facebook Quickstart guide.