Facebook SDK 3.1 iOS: обрабатывать логин, если пользователь удаляет приложение из настроек Facebook
Я хочу добавить некоторую интеграцию Facebook в свое приложение. На этом этапе мне удалось войти в систему, опубликовать на стене друзей, получить список друзей и т.д. Все в порядке, за исключением одной вещи...
Если пользователь удаляет приложение из ваших настроек/приложений Facebook
а затем входит в приложение iOS, код не распознает, что приложение Facebook было удалено из пользовательских настроек и предполагает, что он зашел в систему (это проблема, потому что если пользователь пытается отправить сообщение на стену друга, приложение ничего).
Затем пользователь закрывает приложение iOS и перезапускает его... С этой перезагрузкой приложение iOS фиксируется и обнаруживает, что пользователь больше не вошел в систему.
Я не могу обнаружить момент сразу после того, как пользователь удалит приложение facebook из настроек, чтобы принести пользователю поток входа...
Вот мой код:
На первой сцене моего приложения...
if([FBSession activeSession].state == FBSessionStateCreatedTokenLoaded)
{
NSLog(@"Logged in to Facebook");
[self openFacebookSession];
UIAlertView *alertDialog;
alertDialog = [[UIAlertView alloc] initWithTitle:@"Facebook" message:@"You're already logged in to Facebook" delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
[alertDialog show];
[alertDialog release];
return YES;
}
else{
NSLog(@"Not logged in to Facebook"); //Show the login flow
return NO;
}
Вот код для openFacebookSession
-(void)openFacebookSession
{
NSArray *permissions = [[NSArray alloc] initWithObjects:
@"publish_stream",
nil];
[FBSession openActiveSessionWithPublishPermissions:permissions defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
[self sessionStateChanged:session state:status error:error];
}];
}
Код для sessionStateChanged...
-(void)sessionStateChanged:(FBSession *)session state:(FBSessionState)state error:(NSError *)error
{
switch (state) {
case FBSessionStateOpen: {
NSLog(@"Session opened");
}
break;
case FBSessionStateClosed:
case FBSessionStateClosedLoginFailed:
[FBSession.activeSession closeAndClearTokenInformation];
break;
default:
break;
}
if (error) {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:@"Error"
message:error.localizedDescription
delegate:nil
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alertView show];
}
}
Большое спасибо!
Ответы
Ответ 1
Я обнаружил, что это происходит и с самим собой... Это было обнаружено, когда пользователь изменил свой пароль в facebook, и мы больше не могли аутентифицироваться. Выполнение ручной повторной синхронизации/очистки сеанса учетной записи позволило им снова войти в систему.
-(void)syncFacebookAccount
{
[self forceLogout];
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountTypeFB = [accountStore accountTypeWithAccountTypeIdentifier:@"com.apple.facebook"];
if (accountStore && accountTypeFB) {
NSArray *fbAccounts = [accountStore accountsWithAccountType:accountTypeFB];
id account;
if (fbAccounts && [fbAccounts count] > 0 && (account = [fbAccounts objectAtIndex:0])) {
[accountStore renewCredentialsForAccount:account completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
// Not actually using the completion handler...
}];
}
}
}
Ответ 2
На самом деле очень сложно разобрать и обнаружить эти типы ошибок по двум причинам:
- Я не могу найти способ обнаружить эту проблему, пока вы на самом деле не попытаетесь и не выполните FBRequest (или подобное), и
- Объект
NSError
, переданный из неудавшегося FBRequest
, НЕИСПРАВНО трудно разбирать и использовать любым функциональным способом.
Ниже приведен сумасшедший метод, который я написал, в то время как буквально вытащил все, чтобы справиться с этим. Он обрабатывает объект NSError *error
от неудачной попытки FBRequest
и передает его соответствующим методам или отображает наиболее разумную ошибку, которую я мог бы найти (или попадает в нижнюю точку).
Обратите внимание на комментарии - особенно около innerError
и parsedResponse
- подробно расскажите, что я обнаружил до сих пор. Удачи, храбрый солдат:
- (void)handleFacebookError:(NSError *)error
withPermissionType:(RMFacebookPermissionsType)type // this is just a typedef enum specifying Write or Read permissions so I can react accordingly
withCompletion:(void (^)(BOOL retry))completionBlock {
newMethodDebugLog;
NSParameterAssert(error);
NSParameterAssert(type);
NSParameterAssert(completionBlock); // the completion block tells the controller whether the error is 'fatal' or can be recovered - if YES, it can be recovered
// this is the parsed result of the graph call; some errors can appear here, too, sadly
NSDictionary *parsedResponse = [error.userInfo objectForKey:@"com.facebook.sdk:ParsedJSONResponseKey"];
int parsedErrorCode = [[[[parsedResponse objectForKey:@"body"]
objectForKey:@"error"]
objectForKey:@"code"]
intValue];
// this is an instance of NSError created by Facebook; it contains details about the error
NSError *innerError = [error.userInfo objectForKey:@"com.facebook.sdk:ErrorInnerErrorKey"];
// innerError is usually un-recoverable
if (innerError) {
// innerError seems to be the response given in true HTTP problems;
DebugLog(@"______innerError FOUND______");
DebugLog(@"innerError: %@",innerError);
DebugLog(@"innerError.code: %d",innerError.code);
// digging deep enough, you can actually find a coherent error message! :D
DebugLog(@"innerError.localizedDescription: %@",innerError.localizedDescription);
if (![alert isVisible]) {
NSString *errorString = @"Facebook Connection Failed";
NSString *okString = @"OK";
alert = [[UIAlertView alloc] initWithTitle:errorString
message:innerError.localizedDescription
delegate:nil
cancelButtonTitle:okString
otherButtonTitles:nil];
[alert show];
} else {
DebugLog(@"Alert already showing!");
}
completionBlock(NO);
} else if (parsedResponse &&
parsedErrorCode != 2) { // I honestly forget what error 2 is.. documentation fail :(
// parsedResponses can usually be recovered
DebugLog(@"parsed response values: %@",[parsedResponse allValues]);
switch (parsedErrorCode) {
case 2500:
case 200:
case 190:
{
DebugLog(@"parsedError code hit! forcing re-login.");
// all errors in case 190 seem to be OAuth issues
// http://fbdevwiki.com/wiki/Error_codes#Parameter_Errors
// if needed, "error_subcode" 458 == user has de-authorized your app
// case 2500 reported while grabbing from a photo album & not logged in
// case 200 "requires extended permission: publish_actions"
if (type == RMFacebookPermissionsTypeRead) {
[self _getFacebookReadPermissionsWithUI:YES
completion:completionBlock];
} else if (type == RMFacebookPermissionsTypeWrite) {
[self _getFacebookWritePermissionsWithUI:YES
completion:completionBlock];
}
break;
}
default:
completionBlock(YES);
break;
}
} else {
if (![alert isVisible]) {
NSString *errorString = @"Facebook Error";
NSString *messageString = @"Mixture Photos was unable to connect to Facebook on your behalf. This is usually a temporary problem. Please try again later.";
NSString *okString = @"OK";
alert = [[UIAlertView alloc] initWithTitle:errorString
message:messageString
delegate:nil
cancelButtonTitle:okString
otherButtonTitles:nil];
[alert show];
} else {
DebugLog(@"Alert already showing!");
}
completionBlock(NO);
}
}
Ответ 3
У меня такая же проблема и не удалось найти правильную документацию о кодах ошибок на сайте SDK на Facebook.
Я решил проблему, сравнив значение [error code];
или [error userInfo];
.
В вашем сеансе будет состояние FBSessionStateClosedLoginFailed
, а словарь userInfo ошибки будет иметь следующую форму
"com.facebook.sdk:ErrorLoginFailedReason" = "com.facebook.sdk:ErrorLoginFailedReason";
С другой стороны, код ошибки показывает мне 2
, чтобы вы могли обработать его в конце sessionStateChanged: function
- (void)sessionStateChanged:(FBSession *)session
state:(FBSessionState)state
error:(NSError *)error {
switch (state) {
case FBSessionStateOpen: {
//update permissionsArrat
[self retrieveUSerPermissions];
if (!needstoReopenOldSession) {
//First User information
[self getUserInformation:nil];
}
NSNotification *authorizationNotification = [NSNotification notificationWithName:facebookAuthorizationNotification object:nil];
[[NSNotificationCenter defaultCenter] postNotification:authorizationNotification];
}
case FBSessionStateClosed: {
break;
}
case FBSessionStateClosedLoginFailed: {
[FBSession.activeSession closeAndClearTokenInformation];
break;
}
default:
break;
}
if (error)
{
NSNotification *authorizationNotification = [NSNotification notificationWithName:faceBookErrorOccuredNotification object:error];
[[NSNotificationCenter defaultCenter] postNotification:authorizationNotification];
}
}