Facebook 3.1.1 SDK breaks A LOT

Naomi,

Thank you for all your testing and confirmation. I also verified that the error doesn’t occur in build 990 but does happen in build 1013 so it’s related to the new Facebook SDK or Corona’s integration.

From my testing and reading, it looks like Facebook made some changes in the Login process. I determined that the error is coming from the permissions used in the Login. I can trigger the error every time by logging in and getting User info or posting a message. If I then log out and log back in, the error will happen on the first or second Facebook access. If I remove the permissions from the Logon, I never see the error. Changing to a different permission doesn’t change anything.

Logging out and back in, is the key to when I saw the error happen. Internally the permissions are only sent to Facebook when you call Login AND the session is not active (e.g., logged out).

It seems that Facebook wants you to set permissions the first time you log in a user and not for every log in after that. You would need to add back permissions if the user cancelled your App’s access or needed to add additional permissions. (You must log out and log back in to set new/additional permissions.)

Under the hood there is no need to call the FB Login API for every command, except when your app is started or you determined that the user has logged out. You must call the Login API when the app first starts because that’s what initializes the FB Connect module. Calling the Login API (without permissions) does no harm because it only does an actual FB login if it finds the session isn’t active (e.g., the user is not logged in).

Can you try implementing the above sequence (not adding any permissions to the Login, except for the first time) to see if that works for you with the latest Corona build?

I’m still looking at this, but I’m not sure there is anything we can fix internally. This may be a change in the way Facebook wants to handle Login, so changing how we call the interface may be all that’s needed to keep the error from happening.

One other note about Facebook errors. The Corona sample code will have a Lua runtime error if FB returns an error. That’s because it’s treating the event.response as a json table when it’s really an error string. I posted the fix in comment #41.

I should note that we are using the latest Facebook SDK without modification. We add code to interface Corona to their SDK but we can’t troubleshoot or debug issues that may be in their code. For most third party libraries that are used in Corona, if we find bugs we try to report them but need to wait for the author to fix them. I don’t know if that’s the case with Facebook, but I’m sure there are bound to be bugs/errors (as found in all code).

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

Hi Tom, thank you for the detailed note. I appreciate you taking the time to explain. From what you noted, it sounds like I should further edit my code to achieve the behavior I want. I’ll give it a shot and see if I can make my app built with 1008 to flow the same way as it does with 990. I will report back once I have a chance to work on it.

Thanks again!

Naomi

Edit: BTW, I will download 1013 just to make sure I’m working with the same build. [import]uid: 67217 topic_id: 34416 reply_id: 140545[/import]

I am seeing memory leaks every time the Sample Code makes a Facebook Request. The ones below and some other similar ones. Should I post a bug report? Will Apple reject apps because of this leak?

FBRequest (64 Bytes) UIKit forwardTouchMethod  
\_\_NSCFString (64 Bytes) Foundation -[NSString initWithUTF8String:]  

[import]uid: 73434 topic_id: 34416 reply_id: 140568[/import]

Ok, Tom, if you find out that this is indeed a bug with the Facebook SDK, please let me know the exact steps to reproduce, and possibly an xcode test project (not tied to corona). I just went off a Skype meeting with Facebook, and I mentioned that we’re having some problems with the latest ios sdk. They’d like to look into it if it turns out to be a bug in the SDK.

One more thing. Why is it not possible to completely log out of facebook in corona? when calling facebook.logout(), I expect that the next time I call facebook.login(), I’m presented with the login dialog where I enter email and password. Instead I’m sent directly to the authorize screen. This makes it impossible to use 2 or more Facebook accounts in a Corona game. [import]uid: 21746 topic_id: 34416 reply_id: 141258[/import]

@Corona: Since there seems to be cleanup work going on with Facebook, you guys should make your Facebook SDK handle things gracefully if the user revokes access to an app.

Do the following:

  1. Start the app and do something that requires Facebook (wall post, etc.)
  2. Log on to Facebook from a computer, and revoke access to the app for the user in question
  3. Try to repeat step 1.

Now, things like “HTTP 400” or an “Access revoked” JSON-object will be returned, and us poor developers have to manually do facebook.logout() and redo the facebook.login() cycle in an attempt to set things straight.

Instead, Corona should elegantly do under the hood what it must to automatically present the user with Facebook’s “Grant app access to Facebook” page again. The developer shouldn’t have to handle this case at all.

If access is revoked, the “Grant access” page should appear automatically the next time the user tries to use Facebook. Simple as that .
[import]uid: 73434 topic_id: 34416 reply_id: 141259[/import]

@olav.morkrid,

I think returning the error code indicating that the user revoked the permission is the right thing to do instead of logging back in to the user’s account automatically. As a user of an app if I revoke permission, it may mean I don’t want your app to access my account. Your app should recognize that and ask the user if he/she still wants to enable Facebook.

Maybe we can handle things better but going through the extra steps should be required (just my opinion).

Not all Facebook 400 errors are because of problems in Corona. From searching the Internet, that error is a general error for things Facebook doesn’t like (e.g., posting to many times within a short period of time). I’m still looking into the errors that have been reported here so I haven’t ruled anything out yet.

@haakon,
It’s my view that logging out of Facebook using the Corona logout API just affects the Corona app. Facebook’s SSO (single sign on) means it uses the current account that’s active on the device. If you are signed into one Facebook account using the Facebook app, I wouldn’t expect signing out of a Corona app would sign you out of the Facebook app. You would need to sign out of your account (Facebook app) to sign into another account with the Corona app. I believe this is built in to the Facebook SDK.

Like I said before, we are still looking into this.

-Tom [import]uid: 7559 topic_id: 34416 reply_id: 141260[/import]

Tom, I tested this just now using the xcode simulator. It has no Facebook app. I also checked under Settings => Facebook, and I have no credentials entered there. I know our support is getting asked about this frequently, so it’s obvious that the users too expects logout to make it possible to use a different Facebook account when logging in again.

I’d say this needs to be handled by Corona. [import]uid: 21746 topic_id: 34416 reply_id: 141262[/import]

And here’s how you should implement it:

http://stackoverflow.com/questions/13457625/facebook-ios-sdk-logout

You need to clear the safari cookie when facebook.logout() is called.

EDIT - there is a sample project in the facebook sdk download, “SwitchUser” that may give you an idea:

"From your context, I’m assuming your device(s) does not have the Facebook app installed nor do you expect to use iOS 6 system authentication which would leave the default login behavior to use Safari. If you were to clear the Safari cookies, that should work but for a smoother experience in your scenario you should use the FBSession openWithBehavior:completionHandler: method and specify a behavior of FBSessionLoginBehaviorForcingWebview so that it uses the inline webview dialog for authentication.

See the SwitchUserSample in the Facebook iOS SDK for an example since that sample demonstrates an app that can toggle between multiple accounts." [import]uid: 21746 topic_id: 34416 reply_id: 141264[/import]

Regarding the Error 400 caused by posting too many times within a short period of time, I believe it isn’t the cause for CASE 5 or CASE 7. If it is, then why will it not trigger the error again after restarting the app? For example, once the app gets error under CASE 7 and the app is restarted, posting high score one after the other does not trigger Error 400. Every high score gets posted to FB and friends scores are retrieved every time thereafter.

BTW, I found that wiping the device is not necessary for this error to occur. I moved on to install the app, go through CASE 7, restart the app and normalize the app, and then delete the app (without logging out of FB and without de-authorizing the app from the user’s Facebook account,) reinstall the app, go through CASE 7 again, and the exact same error occurs in the exact same way.

Naomi

[import]uid: 67217 topic_id: 34416 reply_id: 141268[/import]

@Tom: Maybe I was unclear. I’ll try to explain my view on this again:

If access is revoked, and the user tries to use Facebook functionality, then the user should be presented with Facebook’s standard “Do you want to grant access to this app?” again, without any unnecessary or confusing error dialogs or failures of any kind.

It should be just like the first time the user tries to couple the app with his Facebook account: The standard “Grant access” page should be presented, because after all, the user tries to use something with Facebook, so that must mean he changed his mind, and wants to access Facebook even though he revoked access earlier on, and thus we should help him proceed.

[import]uid: 73434 topic_id: 34416 reply_id: 141269[/import]

I implemented this myself in our nativefacebook enterprise plugin that contains a lot of missing Corona=>Facebook implementations. Works like a charm. [import]uid: 21746 topic_id: 34416 reply_id: 141270[/import]

Hey, haakon, if you are annoyed by me making giant posts under this thread, please let me know, and I’ll move it to a new thread. (I think it’s related, and so I think it’s okay, but I never know how others feel & think, and I have no intention of offending you.)

@Tom, I was thinking of completely redoing my FB related code so that there’s only a single instance of facebook.login call after the app is launched. If I implement it that way, my app wouldn’t make facebook.login call twice. It will make the facebook.login call only if the app is killed and restarted (or when the user taps on the FB connect button the first time.) But from what you posted on #47, I’m not sure if it would make any difference. With both CASE 5 and CASE 6, I make facebook.login call with permission only once (at the time of signing in and authorizing the app.) All other facebook.login calls do not include permission (because every possible permissions required for the app is now included in the very first facebook.login call.) The app also never logs out of FB in both CASE 5 and CASE 6. So my finding is different from what you found to be the issue. So I’m wondering if it’s worth the trouble removing second and subsequent facebook.login calls.

Besides, how would the app know if/when user deauthorizes the app from his/her FB account or logs out of FB when these things are done outside the app. If I’m not calling facebook.login, but just facebook.request, I’m assuming that the event.type would only return “request” and not “session”… Does facebook.request return event.phase with “loginFailed” or “loginCancelled” too? If it does, I could work with this and see if single login call would solve all my woes.

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

Wow. No kidding. The well placed facebook.logout actually solved the CASE 6. Maybe it will solve CASE 5 too. Is this what you did, haakon, to work this out? I do remember seeing a post somewhere about the key to solving Facebook problem is the use of facebook.logout. The problem was, I added the facebook.logout everywhere early on, which wildly exacerbated my problem and complicated the debug process for me.

Here’s the solution I found for my app:

  1. add facebook.logout only inside if-statement of an event.phase ~= “login”

  2. call facebook.login with permissions only once after the app is launched
    Note: actually, with my app, I might be call facebook.login with permissions only once per device. Once a user give permission on a given device, until the device is wiped, I might not be calling the login with permission. I need to investigate a bit more before I implement this on all FB related calls, but I suppose it’s enough info for anyone who might be seeking solution here, so I won’t be updating this note.

  3. after facebook.login with permissions is called once (and after the user permits the app’s use of Facebook access), call facebook.login without permission

I think that was all it took. I’m not seeing the problem Tom mentions in his post #47, so who knows, my app might come across that issue (hopefully not, though.) I can also still think of other paths the user may take that could possibly bring back the Error 400 (such as de-authorizing/removing the app directly from the user’s Facebook account while the app is still in suspend mode), but perhaps that’s an edge case I can live with (and perhaps it won’t even cause error 400, who knows – I’m too tired to test every possible case that I can think relating to my FB fiasco at this point.) I’m going to move on and work on finishing up my app (which is way late than I hoped.)

Tom or anyone else who is dealing with this error, please let me know if you find other solution(s). I would also like to hear if there are other error case(s) that cannot be solved with the solution I employed. It can only help us get our Facebook components working smoothly with the updated Facebook SDK.

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

Hi Naomi,

Thank you very much for your hard work here and outlining the different cases you’re come across. I haven’t had time to get back on this but I hope too in the next day or two. It sounds like you confirmed my thoughts that issuing permissions multiple times on the same device is the source of the FB 400 error.

I’m a little confused why you were getting the FB error in your previous post when you only called login once with permissions. You mentioned adding that logout fixed the issue – can you give a little more information where you added that code so I can construct a test case to see what’s going on?

Maybe it would be better to continue the conversation via email and not keep adding to this thread. We can update the thread later when we come to a conclusion about the issue. tom at coronalabs.com

-Tom [import]uid: 7559 topic_id: 34416 reply_id: 140902[/import]

Hi Tom, I’ll follow up with you via email.

Naomi

Edit: Just so I don’t mislead people, I have to add this:
CASE 7 failed, just the same way CASE 5 failed. CASE 7 is equivalent to CASE 5. With CASE 7, only difference from CASE 6 is that the first time user wants to post to FB, he/she needs to login to FB, because he/she skipped on connecting to FB in Scene A. Login call with permission parameter is called only once (just like it does with CASE 5). So I guess facebook.logout isn’t helping. Now what? Dunno what to do. I’ll follow up with Tom and see if he can identify where the problem is and find a fix for it… [import]uid: 67217 topic_id: 34416 reply_id: 140912[/import]

BTW, maybe CASE 6 worked fine every time except that I thought it failed when in fact I was testing CASE 7. The next day, when I tried CASE 6 again, it worked (because perhaps it never failed.)

Anyhow, here’s how CASE 7 went.

CASE 7: Error 400 (app built with1013, installed on a freshly wiped device, with a new user who has not authorized the app on FB)

1 (scene A): facebook.login( myAppID, myFBRegListener, { "“email”, “publish_actions” } )
* User skips on connecting to FB

2 (scene D): facebook.login( myAppID, FBLoginListener, { “email”, “publish_actions” } )
* The user plays a few games and makes a new high score, and decides to post the score on FB
* The app invokes facebook.login( myAppID, FBLoginListener, { “email”, “publish_actions” } )
* Upon successful login to FB, the app invokes facebook.request( “me” )
* Upon successful receipt of the json object with the user data, the app saves the data
* Upon successful save, the app invokes facebook.request(“me/scores” , “POST”, attachment)
* Upon successful post to FB, the app invokes facebook.request(myAppID … “/scores”)
* Upon successful retrieval of score data json object from FB, the app displays the friends scores
* The user then proceed to play another game.

3 (scene D): facebook.login( myAppID, FBScoreListener )
* The user plays a game and beats the previous high score.
* The user chooses to post it to FB and see FB friends score, which invokes facebook.login( myAppID, FBScoreListener )
* Upon successful login to FB, the app invokes facebook.request(“me/scores” , “POST”, attachment)
* Upon successful post to FB, the app tries to invoke facebook.request(appID … “/scores”) but fails with Error: HTTP status code: 400
[import]uid: 67217 topic_id: 34416 reply_id: 140954[/import]

Hi, Tom, where are we with this? Do you think the HTTP Error 400 issue would be fully addressed anytime soon, or do you recommend I seriously consider reverting back to daily build 990? Not so far in the distant future (meaning definitely this month, and perhaps within the next couple of weeks), I’d like to finalize and submit my app for release.

If I can’t expect this error to be fixed (or Corona side to come to some sort of resolution) sometime this week, I would need to revert back to 990 and test the app thoroughly to ensure nothing is breaking as I finalize the app. So please please please let me know the status of things.

Thanks,
Naomi

P.S. If you need more information as to where and how things break, please give me a shout. I’m more than happy to help getting this issue resolved. [import]uid: 67217 topic_id: 34416 reply_id: 141593[/import]

@olav.morkrid, I don’t think it hurts to submit a bug report. About Apple, I doubt it will reject your apps because of this particular leak you found (unless your app accumulates this leak very quickly for some reason.) How often & how quickly do you add the 64 bytes to the memory during the time the user uses your app? If it adds up very rapidly and ends up crashing the app every time you run your app, then it would be a no go (meaning, Apple won’t approve it.) But if it takes hours and months of uninterrupted use of your app, then Apple probably won’t notice.

@Tom, here’s the update to my saga. I was able to sort out the user flow by removing facebook.logout entirely and calling facebook.login only once at the time of entry to the Scene C with rows & rows of FB friends (which the user enters from Scene B with FB Connect/Friends button. And sorry about changing the scene names – I felt I needed to introduce scene C to make my test case easier to understand.)

Anyhow, thanks to your detailed post on #47, I decided the evil is with using facebook.login for the multiple of facebook.request the app makes in a quick succession where the likelihood of user signing out in-between is almost nil. Taking this measure solved the CASE 2 & CASE 4 issue completely. It flows perfectly well, just like how I wanted to have it. With this fix, the user experience is very much the same as the successful user experience achieved with the CASE 4 using daily build 990.

THAT SAID… I don’t know how I may work around the last bit of problem. I still get Error Status 400 under the following scenario. Can you think of any reason why it’s still giving out this error? Is this a Corona bug, or is this a Facebook bug, or am I doing something wrong?

CASE 5: Error 400 (app built with1013, installed on a freshly wiped device with a new user)

1 (scene A): facebook.login( myAppID, myFBRegListener, { "“email”, “publish_actions” } )
* User skips the FB Connect button

2 (scene B): go to scene C
* Because the user has not supplied FB credentials, this scene displays FB Connect button
* Tapping on the FB Connect button transitions the scene to C (using storyboard API)

3 (scene C): facebook.login( myAppID, myFBLoginListener, { “email”, “publish_actions” } )
* Upon entering this scene, facebook.login( myAppID, myFBLoginListener, { “email”, “publish_actions” } ) is invoked.
* Upon successful login, facebook.request( “me” ) is invoked
* Upon successful receipt of the json object with the user data, the app saves the data
* Upon successful save, facebook.request( “me/friends” ) is invoked
* Upon fetching the friends list, the scene C successfully displays the friends list

4 (scene C): facebook.login( myAppID, FBPostListener )
* User taps on a friend
* If the friend is already registered with the app, the app initiates a match game between the players
* If the friend is not already registered with the app, facebook.login( myAppID, FBPostListener ) is invoked
* Upon successful login, facebook.showDialog( “feed”, attachment ) is invoked

5 (scene C): facebook.showDialog( “feed”, attachment )
* This brings up the FB dialog without a problem
* The user taps on Share button, and it posts on the friends wall.
* The user now exits scene C and returns to scene B

6 (scene B): go to scene C
* Now that the app has the user’s FB credential, the button changes from FB Connect to FB Friend
* Tapping on the FB Friend button sends the user to scene C again

7 (scene C): facebook.login( myAppID, myFBListener )
* Now that the app has the user’s FB credential, entering the scene invokes facebook.login( myAppID, myFBListener ) instead of facebook.login( myAppID, myFBLoginListener, { “email”, “publish_actions” } )
* Then the dreadful Error: HTTP status code: 400 shows up.
* User sees a message from the app stating that it could not log in to Facebook.
* Tapping on the message sends the user back to scene B.

8 (scene B & C)
* This turns into never ending loop – i.e., step 6 and step 7 can repeat over and over
* At this point, the user kills the app by tapping on home button, then double tapping on home button, and then closing the app.

9 (scene B) go to scene C
* After the app is restarted, the user goes straight to Scene B
* The app remembers the user’s FB credential and displays FB Friend button
* Tapping on the FB Friend button sends the user to scene C

10 (scene C): facebook.login( myAppID, myFBListener )
* Upon entering the scene, facebook.login( myAppID, myFBListener ) is invoked (just like step 7 above)
* Upon successful login, the listener invokes facebook.request( “me/friends” )
* Upon fetching the friends list, the scene C successfully displays the friends list

After this, moving between scenes B & C causes no error.

How do you think this problem can be solved?

I’m not sure if this means anything, but I also noticed something odd. ( Edit: Could this be related to the phantom Session Bug that haakon mentions in post #3…? ) After I had the never ending loop in step 8, I left the device idle for a while without killing it (mainly because I was writing up this post). And then before killing the app, I tapped on Friend button once again. It brought up FB permission screen. I don’t know why I didn’t tap on Okay for the permission request, but I didn’t. I just killed the app right there and then, which brought me to step 9. Anyhow, it still makes very little sense why Error 400 occurs. I just hope you can identify the issue here, because I can’t think of any work around that makes sense.

Naomi

Saga continues… This is getting really long, but here’s another case where it manifests exactly the same problem. I believe the issue is clear enough at this point, and I’d very much like to hear what you suggest I do with Facebook feature. It really isn’t going to work for me the way it behaves. (Unless, I suppose, I bring back that awkward facebook.logout calls or revert back to daily build 990.)


EDIT: CASE 6 actually works perfectly fine. It appears that I was testing some other path, thinking it was this particular path I was taking. Please see my update in post #55.

CASE 6: Error 400 (app built with1013, installed on a freshly wiped device)

1 (scene A): facebook.login( myAppID, myFBRegListener, { "“email”, “publish_actions” } )
* User taps on the FB Connect button, which invokes facebook.login( myAppID, myFBRegListener, { "“email”, “publish_actions” } )
* Upon successful login, facebook.request( “me” ) is invoked
* Upon successful receipt of the json object with the user data, the app saves the data and go to next scene (using storyboard API)

2 (scene D): facebook.login( myAppID, FBScoreListener )
* The user plays a few games and reaches a new high score.
* The user has a choice to post his/her score and see FB friends scores
* The user chooses to do so, and the app invokes facebook.login( myAppID, FBScoreListener )
* Upon successful login to FB, the app invokes facebook.request(“me/scores” , “POST”, attachment)
* Upon successful post to FB, the app invokes facebook.request(myAppID … “/scores”)
* Upon successful retrieval of score data json object from FB, the app displays the friends scores
* The user then proceed to play another game.

3 (scene D): facebook.login( myAppID, FBScoreListener )
* The user plays a game and beats the previous high score.
* The user chooses to post it to FB and see FB friends score, which invokes facebook.login( myAppID, FBScoreListener )
* Upon successful login to FB, the app invokes facebook.request(“me/scores” , “POST”, attachment)
* Upon successful post to FB, the app tries to invoke facebook.request(appID … “/scores”) but fails with Error: HTTP status code: 400

From then onward, the app performs all non-FB related tasks, but it returns Error 400 for anything FB. Accessing Friends List in Scene C fails with Error 400 as well.

However, once I kill and restart the app, everything normalizes, and posting high scores one game after the other no longer fails. Friends List in Scene C can also be accessed and used as expected.

There is a bug somewhere, but at this point, I’m pretty sure the bug is caused either by the updated Facebook SDK or by how Corona integrated it. [import]uid: 67217 topic_id: 34416 reply_id: 140589[/import]

Ok, Tom, if you find out that this is indeed a bug with the Facebook SDK, please let me know the exact steps to reproduce, and possibly an xcode test project (not tied to corona). I just went off a Skype meeting with Facebook, and I mentioned that we’re having some problems with the latest ios sdk. They’d like to look into it if it turns out to be a bug in the SDK.

One more thing. Why is it not possible to completely log out of facebook in corona? when calling facebook.logout(), I expect that the next time I call facebook.login(), I’m presented with the login dialog where I enter email and password. Instead I’m sent directly to the authorize screen. This makes it impossible to use 2 or more Facebook accounts in a Corona game. [import]uid: 21746 topic_id: 34416 reply_id: 141258[/import]

@Corona: Since there seems to be cleanup work going on with Facebook, you guys should make your Facebook SDK handle things gracefully if the user revokes access to an app.

Do the following:

  1. Start the app and do something that requires Facebook (wall post, etc.)
  2. Log on to Facebook from a computer, and revoke access to the app for the user in question
  3. Try to repeat step 1.

Now, things like “HTTP 400” or an “Access revoked” JSON-object will be returned, and us poor developers have to manually do facebook.logout() and redo the facebook.login() cycle in an attempt to set things straight.

Instead, Corona should elegantly do under the hood what it must to automatically present the user with Facebook’s “Grant app access to Facebook” page again. The developer shouldn’t have to handle this case at all.

If access is revoked, the “Grant access” page should appear automatically the next time the user tries to use Facebook. Simple as that .
[import]uid: 73434 topic_id: 34416 reply_id: 141259[/import]