2014-11-06 19 views
22

Ho bisogno di cambiare una funzione per valutare JavaScript da UIWebView a WKWebView. Devo restituire il risultato della valutazione in questa funzione.WKWebView valuta il valore restituito JavaScript

Ora, io chiamo:

[wkWebView evaluateJavaScript:call completionHandler:^(NSString *result, NSError *error) 
    { 
     NSLog(@"Error %@",error); 
     NSLog(@"Result %@",result); 
    }]; 

Ma ho bisogno di portare come valore di ritorno ottenere, come in UIWebView. Puoi suggerire una soluzione?

+0

'NSString * returnVal = [self.webView stringByEvaluatingJavaScriptFromString: @ "func (\" arg \ ")"];' non si questo lavoro? – l0gg3r

+0

No questa funzione è in UIWebView e funziona, ho bisogno di cambiarlo in WKWebView. Posso risolverlo con qualche callback, ma è troppo complicato nel mio progetto. – redak105

+0

hm ... strano, cosa produce la console? after NSLogs – l0gg3r

risposta

25

Ho risolto questo problema attendendo il risultato finché non viene restituito il valore del risultato.

ho usato NSRunLoop per l'attesa, ma non sono sicuro che sia meglio o no ...

Ecco il codice sorgente di categoria estensione che sto usando ora.

@interface WKWebView(SynchronousEvaluateJavaScript) 
- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script; 
@end 

@implementation WKWebView(SynchronousEvaluateJavaScript) 

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script 
{ 
    __block NSString *resultString = nil; 
    __block BOOL finished = NO; 

    [self evaluateJavaScript:script completionHandler:^(id result, NSError *error) { 
     if (error == nil) { 
      if (result != nil) { 
       resultString = [NSString stringWithFormat:@"%@", result]; 
      } 
     } else { 
      NSLog(@"evaluateJavaScript error : %@", error.localizedDescription); 
     } 
     finished = YES; 
    }]; 

    while (!finished) 
    { 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
    } 

    return resultString; 
} 
@end 

codice di esempio)

NSString *userAgent = [_webView stringByEvaluatingJavaScriptFromString:@"navigator.userAgent"]; 

NSLog(@"userAgent: %@", userAgent); 
+0

Usa stringByEvaluatingJavaScriptFromString: nome invece di lasciare intatto il codice UIWebView esistente. –

+3

Il problema con questo codice è che se viene generato un errore JS, la funzione nativa verrà eseguita all'infinito! La soluzione sarebbe controllare un blocco sicuro al posto di resultString. Guarda sotto – Brams

+0

Segnato questo per essere il primo (e terminare una mattinata completa di WTF). Si prega di aggiornare questa risposta per utilizzare il controllo BOOL isFinished annotato di seguito per 5 stelle. – eGanges

16

Questa soluzione funziona anche se il codice del javascript sollevare NSError:

- (NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script { 
    __block NSString *resultString = nil; 
    __block BOOL finished = NO; 

    [self evaluateJavaScript:script completionHandler:^(id result, NSError *error) { 
     if (error == nil) { 
      if (result != nil) { 
       resultString = [NSString stringWithFormat:@"%@", result]; 
      } 
     } else { 
      NSLog(@"evaluateJavaScript error : %@", error.localizedDescription); 
     } 
     finished = YES; 
    }]; 

    while (!finished) 
    { 
     [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; 
    } 

    return resultString; 
} 
10

Ho appena imbattuto circa lo stesso problema e ho scritto un po 'di Swift (3.0) Estensione WKWebView per esso, ho pensato di poterlo condividere:

extension WKWebView { 
    func evaluate(script: String, completion: (result: AnyObject?, error: NSError?) -> Void) { 
     var finished = false 

     evaluateJavaScript(script) { (result, error) in 
      if error == nil { 
       if result != nil { 
        completion(result: result, error: nil) 
       } 
      } else { 
       completion(result: nil, error: error) 
      } 
      finished = true 
     } 

     while !finished { 
      RunLoop.current().run(mode: .defaultRunLoopMode, before: Date.distantFuture) 
     } 
    } 
} 
+0

Non riesco a utilizzare il codice a causa di RunLoop. Il mio compilatore non riesce a trovare la variabile RunLoop. Ho importato WKWebKit e Foundation ... Quando uso NSRunLoop.currentRunLoop(). Run() continua a funzionare, e non posso dire per quanto tempo correre. Che cosa sto facendo di sbagliato? –

+0

Ho usato: NSRunLoop.currentRunLoop(). RunMode ("NSDefaultRunLoopMode", beforeDate: NSDate.distantFuture()) ha funzionato! Grazie per la tua risposta!!! –

2

Ho trovato che il valore dell'istruzione finale nel javascript immesso è il valore restituito passato come argomento id alla funzione di completamento, se non ci sono eccezioni. Così, per esempio:

[self.webview evaluateJavaScript:@"var foo = 1; foo + 1;" completionHandler:^(id result, NSError *error) { 
    if (error == nil) 
    { 
     if (result != nil) 
     { 
      NSInteger integerResult = [result integerValue]; // 2 
      NSLog(@"result: %d", integerResult); 
     } 
    } 
    else 
    { 
     NSLog(@"evaluateJavaScript error : %@", error.localizedDescription); 
    } 
}];