How to integrate Facebook's JavaScript SDK with GWT

First of all - you need to have a Facebook account. Then you need to create an application on Facebook at http://www.facebook.com/developers/createapp.php, that will give you an App Id. This id is tightly connected to the url/site from which you will be using the JavaScript SDK and you’ll be using it later.
For development, localhost addresses works fine - i.e. http://127.0.0.1:8888.
Next thing is to add the actual JavaScript library to you hostpage, which according to Facebook means adding this piece of JavaScript just before the body-endtag:
<!-- Facebook integration -->
<div id="fb-root"></div>
<script>
(function() {
var e = document.createElement('script'); e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>
It will dynamically add a script-tag into your html-file, which bypasses the “Same Origin Policy” all together. (No need for a xd_receiver.htm, as with previous versions of the API, that is).
Now it’s time to initialize the Facebook API using native methods in GWT:
private native String initFacebookAPI()
/*-{
$wnd.FB.init({appId: '<YOUR_APP_ID_HERE>', status: true, cookie: true, xfbml: true});
$wnd.FB.Event.subscribe('auth.sessionChange', function(response) {
if (response.session) {
// A user has logged in, and a new cookie has been saved
$wnd.onLogin();
} else {
// The user has logged out, and the cookie has been cleared
$wnd.onLogout();
}
});
}-*/;
$wnd.FB works because we dynamically included the Facebook .js-file in the previous step. So, anything you’d want to do when initializing the API can be done here, e.g. adding subscriptions to events (as above).
As can be seen, two different methods are being called when a sessionChange-event occurs (depending on what happened). These methods will be regular GWT-methods, but we need to export them into the DOM in order to call them from within native code:
private native void exportMethods(Application instance) /*-{
$wnd.onLogin = function() {
return instance.@com.technowobble.gwt.client.Application::onLogin()();
}
$wnd.onLogout = function() {
return instance.@com.technowobble.gwt.client.Application::onLogout()();
}
$wnd.onAPICall = function(callback, response, exception) {
return instance.@com.technowobble.gwt.client.Application::onAPICall
(Lcom/google/gwt/user/client/rpc/AsyncCallback;Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;)
(callback, response, exception);
}
}-*/;
There’s also on more function being exported (which needs some explanation…) It serves as a callback function for a native method exposing the Facebook Graph API:
private native void callAPI(String path, AsyncCallback<JavaScriptObject> callback) /*-{
$wnd.FB.api(path, function(response) {
if (!response) {
alert('Error occured');
} else if (response.error) {
alert($wnd.dump(response));
// call callback with the actual error
$wnd.onAPICall(callback, null, response.error);
} else if (response.data) {
alert($wnd.dump(response));
// call callback with the actual json-array
$wnd.onAPICall(callback, response.data, null);
} else {
alert($wnd.dump(response));
// call callback with the actual json-object
$wnd.onAPICall(callback, response, null);
}
});
}-*/;
What happens here is that any GWT-method now can make calls like this:
callAPI("/me", new AsyncCallback<JavaScriptObject>() {
@Override
public void onFailure(Throwable caught) {
Window.alert(caught.getMessage());
}
@Override
public void onSuccess(JavaScriptObject result) {
UserJso fbUser = (UserJso) result;
Window.alert(fbUser.getFullName());
}
});
The onAPICall exported previously is the method binding the first call to the AsyncCallback:
public void onAPICall(AsyncCallback<JavaScriptObject> callback,
JavaScriptObject response, JavaScriptObject exception) {
if (response != null) {
callback.onSuccess(response);
} else {
ExceptionJso e = (ExceptionJso) exception;
callback.onFailure(new Exception(e.getType() + ": " + e.getMessage()));
}
}
Note that the AsyncCallback used for a specific Graph API call, e.g. “/me” is free to cast the result to an overlay type (the result should be a json-representation). The UserJso looks like this:
package com.technowobble.gwt.client.json;
import com.google.gwt.core.client.JavaScriptObject;
public class UserJso extends JavaScriptObject {
// Overlay types always have protected, zero-arg constructors
protected UserJso() { }
public final native String getFirstName() /*-{ return this.first_name; }-*/;
public final native String getLastName() /*-{ return this.last_name; }-*/;
public final String getFullName() {
return getFirstName() + " " + getLastName();
}
}
A full example project can be found here and should get you started on more elaborative usage.