Как мога да накарам UIWebView да отвори Facebook страница за вход в отговор на OAuth заявка на iOS 5 и iOS 6?

Имаме:
(1) Уеб приложение, базирано на Facebook API, с функционалност Facebook OAuth („уеб приложението на FB“)
(2) Базиран на UIWebView браузър на iPad („Браузърът“)

Нашата цел е да отворим страницата за вход във Facebook, за да влезем в уеб приложението на FB (1) в базирания на UIWebView браузър (2) на iPad.

Тук има донякъде подобен проблем:
http://facebook.stackoverflow.com/questions/11337285/no-longer-able-to-login-to-ios-app-via-oauth-the-page-requested-was-not-found

Проблемът с този въпрос обаче възниква, след като потребителят въведе потребителско име и парола във формуляра на Facebook. Нашият проблем е, че не можем да покажем формуляра за влизане във Facebook на първо място. Промяната на типа приложение от „Web“ на „Native/Desktop“, както беше предложено в този въпрос, не помогна.

Стъпки:
1. Отворете нашата уеб страница (обикновена HTML страница) с този UIWebView браузър
2. Щракнете върху бутона за стартиране на „FB web app“ на тази страница
3. OnClick JavaScript се опитва да инициира OAuth, което трябва да отвори екрана за вход във Facebook, за да влезе в уеб приложението на FB

Текущ резултат (проблем):
На устройства с iOS 5.+ и iOS 6.+
- Нашата уеб страница остава непроменена
- Страницата за вход във Facebook НЕ се показва (нашата уеб страница все още се показва)
На iOS 4.3 (работи според очакванията):
- страницата за вход във Facebook се отваря в същия UIWebView обект на браузъра (замества нашата уеб страница)

Очакван резултат:
- Показва се страницата за вход във Facebook и потребителят може да въведе потребителско име и парола за Facebook
- Работи на iOS 5.+ и iOS 6.+, ако се стартира в браузъра Safari на iPad. Страницата за вход във Facebook се отваря в отделен раздел (за разлика от тях в UIWebView няма отделни раздели)

Въпрос: Как мога да накарам UIWebView да отвори Facebook страница за вход в отговор на OAuth заявка на iOS 5+ и iOS 6+?

Още технически подробности:

Регистрираме различни NSURLRequest полета отвътре

-(BOOL)webView(UIWebView*)webView shouldStartLoadWithRequest(NSURLREquest*)request navigationType:…   

И забелязваме известна разлика в регистрационните файлове за „правилно“ и „неправилно“ поведение. Ето как изглеждат потоците на изпълнение за мен:

Първо натискам бутона за стартиране на „FB Web App“, за да инициирам OAuth, след което някои случаи изчезват

iOS 4.3, „правилна“
заявка до www.facebook.com/dialog/oauth?...
заявка до fbwebapp.com
заявка до m.facebook.com/login.php?... .
--тук се появява вход във facebook

iOS 5.0, “incorrect1”
заявка до www.facebook.com/dialog/oauth?...
заявка до fbwebapp.com
заявка до m.facebook.com/login.php?...

Тогава може да е
--много m.facebook.com/login.php?...със next... в параметрите
последвано от sqlite грешка
--точно сега виждам „Съжалявам, нещо се обърка” страница от facebook (за първи път я срещам)

iOS 6.0 “incorrect2”
заявка до www.facebook.com/dialog/oauth?...
заявка до fbwebapp.com

-(void)webView:(UIWebView*)webView didFailLoadWithError:(NSError*)грешка се извиква с код на грешка -999

Можете да видите, че поведението определено зависи от версията на iOS. Но често срещаният случай е грешката да се случи на стъпката за получаване на m.facebook.com/login.php.. URL. Но това е всичко, което можем да открием.

Цял ден си удряме главите в стената в търсене на решения. Безнадеждно.
Можете ли да ни помогнете да отворим страницата за вход във Facebook в UIWebView в отговор на OAuth?


person user1812802    schedule 09.11.2012    source източник


Отговори (5)


просто използвайте: този код

if (![FBSDKAccessToken currentAccessToken]) 
{   
    FBSDKLoginManager *manager = [[FBSDKLoginManager alloc]init];
    manager.loginBehavior = FBSDKLoginBehaviorWeb;
    [manager logInWithReadPermissions:@[@"public_profile", @"email", @"user_friends"] handler:
     ^(FBSDKLoginManagerLoginResult *result, NSError *error) {

         NSLog(@"result.token: %@",result.token.tokenString);  
         NSLog(@"%@",result.token.userID);   
         NSLog(@"%hhd",result.isCancelled);     
     }];
}

// тук manager.loginBehavior = FBSDKLoginBehaviorWeb; е всичко, от което се нуждаете, за да отворите facebook в UIWebview

person Navpreet    schedule 21.05.2015

Го е направил!

Това е нещо като хак, но js facebook sdk влизането в UiWebView в iOS 6 най-накрая работи.

Как би могло да се направи? Това е чисто JS + Facebook JS SDK + UIWebView Delegate решение за обработка на функции.

JS - Първа стъпка)

бутон за влизане (за свързване с facebook) извиква този пример за функция, който ще задейства диалогови прозорци за вход/oauth на Face JS:

function loginWithFacebookClick(){

FB.login(function(response){
    //normal browsers callback 
});

}

JS - Втора стъпка)

добавете слушател authResponseChange всеки път, когато потребителят зареди уеб страницата (след FB.init()), за да улови свързаното състояние на потребителя:

FB.Event.subscribe('auth.authResponse.Change', function(response){
//UIWebView login 'callback' handler
var auth = response.authResponse;
if(auth.status == 'connected'){
    //user is connected with facebook! just log him at your webapp
}
});

И с функциите за делегиране на UIWebView на приложението можете да обработвате oauth отговорите на Facebook

Цел C - Трета стъпка)

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSString *url = [[request URL] absoluteString];

//when user status == connected (has a access_token at facebook oauth response)
if([url hasPrefix:@"https://m.facebook.com/dialog/oauth"] && [url rangeOfString:@"access_token="].location != NSNotFound)
{
    [self backToLastPage];
    return NO;
}

return YES;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView
{

NSString *url = [[webView.request URL] absoluteString];

if([url hasPrefix:@"https://m.facebook.com/dialog/oauth"])
{
    NSString *bodyHTML = [webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"];

    //Facebook oauth response dead end: is a blank body and a head with a script that does 
    //nothing. But if you got back to your last page, the handler of authResponseChange
    //will catch a connected status if user did his login and auth app
    if([bodyHTML isEqualToString:@""])
    {
        [self backToLastPage];
    }
}

}

Така че, когато „пренасочи“ потребителя към последната заредена страница, втората стъпка ще обработи действието на потребителя в диалоговите прозорци за влизане във Facebook.

Ако съм избързал с този отговор, моля, попитайте ме! Дано помогне.

person owlmsj    schedule 21.07.2013
comment
backToLastPage означава, че [webview goBack] ? - person iajnr; 13.05.2018

В случай, че някой търси в Гугъл, ето какво работи при мен:

-(BOOL) webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)inType {
    if ([request.URL.absoluteString containsString:@"m.facebook.com"]) {
        if ([request.URL.absoluteString rangeOfString:@"back"].location == 0) {
            [self.popUp removeFromSuperview];
            self.popUp = nil;
            return NO;
        }
        if (self.popUp) {
            return YES;
        }

        UIWebView *wv = [self popUpWebView];
        [wv loadRequest:request];
        return NO;
    }
    return YES;
}

- (UIWebView *) popUpWebView {
    toolbar height
    UIWebView *webView = [[UIWebView alloc]
            initWithFrame:CGRectMake(0, 0, (float)self.view.bounds.size.width,
                    (float)self.view.bounds.size.height)];
    webView.scalesPageToFit = YES;
    webView.delegate = self;
    // Add to windows array and make active window
    self.popUp = webView;
    [self.view addSubview:webView];
    return webView;
}

- (void)webViewDidFinishLoad:(UIWebView *)webView {

    if (self.popUp) {
        NSError *error = nil;
        NSString *jsFromFile = @"window.close=function(){window.location.assign('back://' + window.location);};";
        __unused NSString *jsOverrides = [webView
                stringByEvaluatingJavaScriptFromString:jsFromFile];

        JSContext *openerContext = [self.webView
                valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        JSContext *popupContext = [webView
                valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
        popupContext[@"window"][@"opener"] = openerContext[@"window"];
    }


  //this is the secret sauce  
  if (webView == self.popUp
            && [webView.request.URL.absoluteString containsString:@"m.facebook.com"]
            && [[webView stringByEvaluatingJavaScriptFromString:@"document.body.innerHTML"] isEqualToString:@""]) {
        [webView stringByEvaluatingJavaScriptFromString:@"eval(document.getElementsByTagName('script')[0].text)"];
    }
}

Извадих куп от тази реализация от тук.

В зависимост от вашето уеб внедряване вероятно ще има една допълнителна стъпка. Скриптът на Facebook всъщност изпълнява window.close(), след това window.open() и след това window.close(). За мен това създаваше проблеми, защото от страна на мрежата, след като това влизане приключи, моят прозорец (т.е. за webView, в който искам потребителят да влезе) получаваше window.close() повикване, идващо от Facebook SDK. Предполагам, че това е така, защото Facebook SDK очаква това window.open() извикване да отвори нов прозорец, който ще затвори.

Тъй като не отменихме функционалността на window.open(), извикването на window.open() няма да направи нищо и Facebook SDK ще се опита да затвори вашия прозорец. Това може да причини всякакви проблеми, но за мен, тъй като използвам Parse, window.localStorage беше зададено на null, така че получавах всякакви грешки.

Ако нещо подобно се случва с вас, имате две възможности да го коригирате:

  1. Ако имате контрол върху уеб кода и не сте готови за малък хак, хвърлете това в window.close=function(){}
  2. Ако нямате контрол над уеб кода, можете или да добавите отмяна на window.close за главния webView, както направихме за popUp webView, или да отмените функцията window.open, за да отворите друг изскачащ прозорец (което е описано в повече подробности тук)
person taylorstine    schedule 13.01.2016
comment
Благодаря за вашето решение. Просто трябва да замените m.facebook.com с mobile.facebook.com. - person Vivek Sinha; 06.04.2018
comment
@taylorstine - не знам защо - Предстои използване на недеклариран идентификатор „JSContext“. - person iajnr; 13.05.2018
comment
а, добре, трябва да се импортира - #import ‹JavaScriptCore/JavaScriptCore.h› - person iajnr; 13.05.2018
comment
Работи перфектно! - person iajnr; 13.05.2018

Използвайте класа FBDialog, за да подканите потребителя да влезе. Това използва уеб изглед вътре в приложението, следователно при успешно влизане потребителят ще влезе вътре във всеки UIWebView:

NSString *kRedirectURL  = @"fbconnect://success";
NSString *kSDK = @"ios" ;
NSString *kLogin = @"oauth";
NSString *kDialogBaseURL = @"https://m.facebook.com/dialog/";

NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                               AE_FACEBOOK_APPID, @"client_id",
                               @"user_agent", @"type",
                               kRedirectURL, @"redirect_uri",
                               @"touch", @"display",
                               kSDK, @"sdk",
                               nil];

NSString *loginDialogURL = [kDialogBaseURL stringByAppendingString:kLogin];

FBLoginDialog* loginDialog = [[FBLoginDialog alloc] initWithURL:loginDialogURL
                                                    loginParams:params
                                                       delegate:self];
[loginDialog show];

След това накарайте вашия клас да се придържа към протокола FBDialogDelegate и добавете тази функция към вашия клас:

-(void)fbDialogLogin: (NSString *)token expirationDate:(NSDate *)expirationDate{
    // Store the token and expiration date into the FB SDK
    self.facebook.accessToken = token;
    self.facebook.expirationDate = expirationDate;
    // Then persist these so the SDK picks them up on next load

    NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];

[defaults setObject:self.facebook.accessToken forKey:ACCESS_TOKEN_KEY];
[defaults setObject:self.facebook.expirationDate forKey:EXPIRATION_DATE_KEY];
[defaults synchronize];
}

HTH!

person Ameesh Kapoor    schedule 11.01.2013
comment
, бихте ли обяснили повече за това решение? Аз съм начинаещ в iOS, но вече изпробвах някои проби на Facebook SDK. Ще се видим - person owlmsj; 07.07.2013

Как да влезете във facebook в UIWebView.

Цел-c

Използвайте отговора на taylorstine. Той ми спаси деня. Благодаря ви taylorstine

Но аз използвам Swift 3. така че току-що преобразувах кода по-долу от отговора на taylorstine.

Swift 3.

func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool {

    if let _ = request.url?.absoluteString.range(of: "m.facebook.com" ) {
        if let _ = request.url?.absoluteString.range(of: "back"){
            self.popUp?.removeFromSuperview()
            self.popUp = nil
            return false
        }

        if let _ = self.popUp {
            return true
        }

        let wv = popUpWebView()
        wv.loadRequest(request)

        return false
    }

    return true
}

func popUpWebView() -> UIWebView {

    let webView = UIWebView(frame: self.view.frame)

    webView.delegate = self
    self.popUp = webView
    self.view.addSubview(webView)

    return webView
}

func webViewDidFinishLoad(_ webView: UIWebView) {
    if let _ = self.popUp {

        let jsFromFile = "window.close=function(){window.location.assign('back://' + window.location);};"

        let _ = webView.stringByEvaluatingJavaScript(from: jsFromFile)

        let openerContext = self.webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext

        let popupContext = webView.value(forKeyPath: "documentView.webView.mainFrame.javaScriptContext") as! JSContext

        popupContext.setObject("opener", forKeyedSubscript: "window" as (NSCopying & NSObjectProtocol)!)
        popupContext.setObject(openerContext.objectForKeyedSubscript("window"), forKeyedSubscript: "opener"  as (NSCopying & NSObjectProtocol)!)

    }

    if webView == self.popUp
        && (webView.request?.url?.absoluteString.range(of:"m.facebook.com") != nil)
        && webView.stringByEvaluatingJavaScript(from: "document.body.innerHTML") == "" {

        webView.stringByEvaluatingJavaScript(from: "eval(document.getElementsByTagName('script')[0].text)")

    }

}
person solgit    schedule 08.02.2017
comment
self.popUp какво е това? - person mihatel; 07.02.2019