Facebook 3.1.1 SDK breaks A LOT

@walter,

the error I found is not related to mixing 2 plugins. Mixing the plugins only affects the native share dialog.

I guess the only way we’ll get facebook integration WITH native share dialogs is to roll our own Facebook plugin from scratch? I was kind of hoping not having to do that, since you have already integrated with Facebook. Our plugin has just got the native share dialog implemented, not anything related to login, sessions or posting stuff to facebook… [import]uid: 21746 topic_id: 34416 reply_id: 142943[/import]

Fixed. Rewrote the plugin to use iOS functionality instead of Facebook functionality:

SLComposeViewController\*fvc = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeFacebook];  
  
...  

With that we were able to switch to daily 1030 and continue presenting the users with native share dialogs. [import]uid: 21746 topic_id: 34416 reply_id: 143042[/import]

Hi Tom,

We are still experiencing problems with build 1028. In the call to facebook.login, we have been wrapping the listener parameter with a lua enclosure to provide context information to the listener:

 facebook.login( game.facebookAppId,  
 function(event)  
 listener(request, event)  
 end,  
 request.permissions  
 )  

The request table includes all of the required data to send to facebook (path, method, attachment), and what action(s) to take upon receipt. This avoids having to define global variables and facilitated concurrent processing of multiple facebook requests.

With the latest build, we can confirm that the enclosure passed to the FIRST login call is now used for all subsequent listener invocations. To some extent, we might be able to work around this, but it will require blocking all user activity until each request is completed. This used to work quite well.

What we really need is the ability to pass a different listener for each facebook.request in the same manner as network.request. The current implementation requires the listener to depend upon global (or at least module level) variables to determine context. [import]uid: 120928 topic_id: 34416 reply_id: 142689[/import]

@cebodine, can you provide some code around the snippet you posted so we can see the context in which it’s used? If you prefer, you can email me directly something that I can test against to better understand the issue. tom at coronalabs dot com.

Have you seen any other issues in your test of 1028?

Thanks,
Tom [import]uid: 7559 topic_id: 34416 reply_id: 142700[/import]

A quick update to let you all know that Daily Build 1028 fixed the issue for me. The final issue that I had with the 1028 was something to do with a device that previously had FB error. I didin’t clean slate the device, and the newly built app got tripped by the lingering issue. Once I wiped the device and then installed the same app, the error went away.

Tom, thank you so much for fixing this!!!

Naomi [import]uid: 67217 topic_id: 34416 reply_id: 142703[/import]

Hi Tom,

I don’t really have time at the moment to put together a full working demonstration that is not our complete project, but here are a few larger snipets… Right now, I am away from my mac, so I cannot build this for device. and test

facebookUI.lua

local facebook = require("facebook")  
local json = require("json")  
  
local factory = {}  
  
-- Facebook Session States  
local fbPhases = {  
 login = "Logged In",  
 loginFailed = "Login Failed",  
 loginCancelled = "Login Cancelled",  
 logout = "Logged Out",  
}  
-- Facebook Connection listener  
--  
local function listener( request, event )  
  
 -- print("facebookUI.listener invoked", dialog, request, event)  
 -- After a successful login event, send the FB command  
 -- Note: If the app is already logged in, we will still get a "login" phase  
 --  
 if ( "session" == event.type ) then  
 -- event.phase is one of: "login", "loginFailed", "loginCancelled", "logout"  
 print( "facebookUI listener, Session Status: " .. event.phase, request )  
 factory.sessionState = event.phase  
 end  
 if ( "session" == event.type ) then  
 if "login" == event.phase then  
  
 print("Login complete, invoking facebook.request", request.path)  
 if request.action then  
 facebook.showDialog( request.action, request.attachment )  
 else  
 facebook.request( request.path, request.method, request.attachment ) -- posting the photo  
 end  
 else  
 print("Login failed: ", event.phase)  
 end  
  
 elseif ( "request" == event.type ) or ( "dialog" == event.type ) then  
 -- event.response is a JSON object from the FB server  
 local response = event.response  
  
 if event.isError then  
 print("Facebook connect error:", json.encode(event))  
 else  
 if "dialog" == event.type and (not(response and string.find(response, "post\_id="))) then  
 print("Dialog cancelled.")  
 if request.onError then  
 request.onError("Canceled")  
 end  
 else  
 print("Facebook Response:",response)  
 local decodedResponse = json.decode(response) or response  
 if decodedResponse and decodedResponse.error then  
 print("Facebook refused request.\n" .. (decodedResponse.error.message or "") )  
 else  
 if request.onResponse then  
 -- invoke callback with decoded json, or plan response if not json encoded  
 request.onResponse(request, decodedResponse)  
 end  
 end  
 end  
 end  
 else  
 print("facebookUI listener - unhandeld event:", event.type)  
 end  
end  
  
function factory.sendMessage( request )  
 -- call the login method of the FB session object, passing in a handler  
 -- to be called upon successful login.  
  
 -- wrap callback to provide context to listener  
 print("invoking facebook.login for ", request.path, request)  
 facebook.login( game.facebookAppId,  
 function(event)  
 listener(request, event)  
 end,  
 request.permissions  
 --{"publish\_stream","user\_photos"}  
 )  
end  

other module:

local fbUI = require( "facebookUI" )  
  
local function getFriendsListener(request, response)  
 local friendsList = response and response.data  
 local contextData = request.contextData  
 -- process the results in friendsList  
end  
  
local function getFriends()  
 local request = {  
 path = "me/friends",  
 attachment = {  
 fields="installed,name,picture.height(90).width(90)"  
 },  
 onResponse = getFriendsListener,  
 onError = function(msg) print("failed", msg) end,  
 contextData = "Other info",  
 }  
 fbUI.sendMessage(request)  
end  

The request table above is just one example, it combines the data to send to facebook with success and error handlers, and can pass additional parameters to the onResponse listener.
It is getting late, I’ve got to go. [import]uid: 120928 topic_id: 34416 reply_id: 142711[/import]

After re-working our code to store the facebook request context globally, our application is now functional on build 1028 (except for the part that attempts to do multiple concurrent facebook requests) I guess I’m going to have to serialize that process and make the user wait.

We have observed a few errors:

Error: HTTP status code: 200  
Facebook connect error: {"isError":true,"type":"request","name":"fbconnect","response":"The operation couldn't be completed. (com.facebook.sdk error 5.)"}  

These have been sporadic, not reproducible, and no pattern has yet been determined. Immediate retry of the same request works.

Thanks [import]uid: 120928 topic_id: 34416 reply_id: 142768[/import]

Facebook error 5 is a general error that’s returned when it couldn’t complete an operation I saw this yesterday when I was testing and the iPad device didn’t have an Internet connection. Your app needs to handle cases when something goes wrong with the connection or talking with the Facebook servers. [import]uid: 7559 topic_id: 34416 reply_id: 142776[/import]

Hey Tom,

I still get Facebook error 5 with my code, which used to work to post a screenshot.

---- \*\*\*\*\*\*\*\*\*\*\* FACEBOOK \*\*\*\*\*\*\*\*\*\*\*\*\* ----  
  
 local function onLoginSuccess()  
  
 local attachment = {  
 message = text[18][language],  
 source = { baseDir=system.DocumentsDirectory, filename="shot.jpg", type="image" }  
 }  
  
 facebook.request( "me/feed", "POST", attachment )  
  
 end  
  
 local function fbListener( event )  
 if event.isError then  
 native.showAlert( "ERROR", event.response, { "OK" } )  
 else  
 if event.type == "session" and event.phase == "login" then  
 -- login was a success; call function  
 onLoginSuccess()  
  
 elseif event.type == "request" then  
 -- this block is executed upon successful facebook.request() call  
  
 native.showAlert( "Success", "The photo has been uploaded.", { "OK" } )  
  
 end  
 end  
 end  
   
 local function postFB(event)  
  
 if event.phase == "began" then   
  
 display.getCurrentStage():setFocus( event.target )  
 event.target.isFocus = true  
  
 elseif event.phase == "moved" then  
  
  
 elseif event.phase == "ended" or event.phase == "cancelled" then  
  
 --\*\* Take screenshot  
 display.save( stillMainMenuGraphicElements[39], "shot.jpg", system.DocumentsDirectory )  
  
 local function waitAndPost()  
 if require("socket").connect("google.com", 80) == nil then  
  
 -- If no internet connection  
  
 else  
  
 facebook.login( fbAppID, fbListener, { "publish\_actions" } )  
  
 end  
 end  
  
 timer.performWithDelay(100, waitAndPost)   
  
 display.getCurrentStage():setFocus( nil )  
 event.target.isFocus = nil  
  
 end  
  
 return true  
end  
  

I did add to the build.settings the following. (However, is it normal to have it once with the “fb” prefix and once without?)

FacebookAppID = "123922354444543",  
  
 CFBundleURLTypes =  
 {  
 {  
 CFBundleURLSchemes =  
 {  
 "fb123922354444543",  
 }  
 }  
 },  
  

Any idea? What am i doing wrong?

Thanks. [import]uid: 100310 topic_id: 34416 reply_id: 142792[/import]

@cebodine, we are looking into your issue to see what we can do to make it work as before. [import]uid: 7559 topic_id: 34416 reply_id: 142794[/import]

@Tom and @cebodine, I’d like to know what Tom means by _ “make it work as before.” _

Does this mean re-working the code _ “to store the facebook request context globally” _ is not necessary? I’ve been re-working my code today, because otherwise, facebook requests are failing after successfully executing a series of requests in one scene (i.e., successfully executing CASE 5 or CASE 7.) If re-working my code is not necessary, I’d rather wait for additional fix before I proceed further. So, is what Tom looking into related to this? Please let me know.

Naomi [import]uid: 67217 topic_id: 34416 reply_id: 142795[/import]

@Naomi,

This means making each call to facebook.login update the listener, whereas in the current implementation (build 1028 and newer), only the first facebook.login sets it. Before build 1028 each login call would update the listener so we introduced a regression bug with our fix. [import]uid: 7559 topic_id: 34416 reply_id: 142799[/import]

@Tom, thank you so much for the explanation. It sounds like I don’t need to rework my code then. That’s a relief.

Thanks again,
Naomi
[import]uid: 67217 topic_id: 34416 reply_id: 142802[/import]

@Tom,

Just to be clear, the listener called should be the listener that was in effect at the time that the request or dialog was sent. That is the only way that multiple concurrent Facebook requests can be correlated.

Ideally, that would mean adding an additional parameter to facebook.request to specify the listener for that specific request.

Thanks [import]uid: 120928 topic_id: 34416 reply_id: 142803[/import]

@cebodine, we are looking to fix what we broke with build 1028 changes. At this time we cannot add new parameters or APIs because it won’t be compatible with Android, which we haven’t updated to the latest SDK. We will update the Android SDK after we get some much needed Android issues out of the way. [import]uid: 7559 topic_id: 34416 reply_id: 142809[/import]

@cebodine, the login listener regression bug is fixed and should be available in the next daily build (1030). [import]uid: 7559 topic_id: 34416 reply_id: 142827[/import]

Hi,

1030 works fine, but broke our own plugin for facebook native share dialog, since you have moved your own code into a plugin (Enterprise).

Error Domain=com.facebook.sdk Code=7 “The operation couldn’t be completed. (com.facebook.sdk error 7.)” UserInfo=0x1ab85fd0 {com.facebook.sdk:NativeDialogReasonKey=NativeDialogInvalidForSession}

Thus we need this piece of code inserted in your plugin asap (we should have submitted to Apple today, but need to wait for you to update):

#import <facebooksdk><br>#import <facebooksdk><br><br>int<br>PluginNativeFacebook::ShowDialogForReal( lua_State *L)<br>{<br> UIViewController *viewController = fRuntime.appViewController;<br> <br> const char *txt = lua_tostring( L, 1 );<br> const char *img = lua_tostring( L, 2 );<br> const char *url = lua_tostring( L, 3 );<br> int listenerIndex = 4;<br> <br> if (NULL == fListener) {<br> if ( CoronaLuaIsListener( L, listenerIndex, kEvent ) )<br> {<br> CoronaLuaRef listener = CoronaLuaNewRef( L, listenerIndex );<br> fListener = listener;<br> }<br> }<br> <br> [FBNativeDialogs<br> presentShareDialogModallyFrom:viewController<br> initialText:[NSString stringWithFormat:@"%s", txt]<br> image:[UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%s", img]]<br> url:[NSURL URLWithString:[NSString stringWithFormat:@"%s", url]]<br> handler:^(FBNativeDialogResult result, NSError *error) {<br> <br> // Only show the error if it is not due to the dialog<br> // not being supporte, i.e. code = 7, otherwise ignore<br> // because our fallback will show the share view controller.<br> if (error &amp;&amp; [error code] == 7) {<br> NSLog(@"Not supported");<br> NSLog(@"%@", error.debugDescription);<br> <br> char status[] = "error";<br> DispatchEvent(true, status);<br> } else {<br> NSLog(@"Supported, what next?");<br> NSString *alertText = @"";<br> if (error) {<br> alertText = [NSString stringWithFormat:<br> @"error: domain = %@, code = %d",<br> error.domain, error.code];<br> <br> } else if (result == FBNativeDialogResultSucceeded) {<br> NSLog(@"Posted successfully");<br> char status[] = "success";<br> DispatchEvent(true, status);<br> } else if (result == FBNativeDialogResultCancelled) {<br> NSLog(@"Cancelled");<br> char status[] = "cancelled";<br> DispatchEvent(true, status);<br> } else if (result == FBNativeDialogResultError) {<br> NSLog(@"Got an error");<br> char status[] = "error";<br> DispatchEvent(true, status);<br> } else if (![alertText isEqualToString:@""]) {<br> NSLog(@"%@", alertText);<br> char status[] = "error";<br> DispatchEvent(true, status);<br> } else {<br> NSLog(@"Just an error, not sure why");<br> char status[] = "error";<br> DispatchEvent(true, status);<br> }<br> }<br> }];<br> <br> return 0;<br>}<br><br>int<br>PluginNativeFacebook::ShowDialog(lua_State *L)<br>{<br> NSLog( @"Show Dialog, Native Facebook Plugin, version %s.", version);<br> <br> Self *provider = ToLibrary(L);<br> provider-&gt;ShowDialogForReal(L);<br> <br> return 0;<br>}<br> [import]uid: 21746 topic_id: 34416 reply_id: 142869[/import]

If the error is generated by the invocation of your code/plugin, then this isn’t an issue with our Facebook implementation.

In other words, if you are calling your own native wrapper for facebook functionality, then our code will not have an impact on your code’s behavior. In fact, none of our code would be executed at all, since you are using your own version instead. [import]uid: 26 topic_id: 34416 reply_id: 142895[/import]

Walter,

you are holding the facebook session in your plugin, which is not accessible from our plugin. It worked perfectly before you moved your facebook code into a plugin.

We can’t force the user to login twice, one time with your plugin and one time with our plugin. So either you must implement the native share dialog in your plugin, or make the facebook session globally available like it was before. Or do you have any other suggestions? [import]uid: 21746 topic_id: 34416 reply_id: 142898[/import]

I’ve tested some more. If I haven’t logged in with Coronas facebook.login, our plugin works. As soon as we log in with facebook.login, the com.facebook.sdk Code=7 error shows up when we try to invoke the native share dialog.

However, with my code example posted, you know as well as I do that implementing the native share dialog is a 10 minute job for you at most, at least in the enterprise static library, now that you have moved the code into a plugin. And it’s even a much voted for feature in your new feature request section…

We’ll submit to Apple with daily 1027 since that works fine for us, but I sure hope you’ll do something about this soon. I mean, it took 7 weeks to solve the com.facebook.sdk Code=5 error, so maybe you can make up for that by implementing the native share dialog today :wink:

Yeah! [import]uid: 21746 topic_id: 34416 reply_id: 142901[/import]