O WebViews do Cocoa fornece um objeto de script pelo qual você pode executar JavaScript a partir do Objective-C e Objective-C a partir do JavaScript.
Infelizmente, obter feedback do JavaScript executado dentro de um WebView não é algo totalmente direto. As exceções são convertidas em undefineds, e você só pode obter um único valor de retorno para usar para depuração.
Não seria ótimo se você pudesse apenas continuar usando as chamadas console.log() de dentro do JavaScript como você está acostumado e ter o resultado exibido no console depurador do Xcode? Boas notícias, galera. Vocês podem! Veja como:
Primeiro, defina um frameLoadDelegate para o seu WebView. Eu só vou usar o aplicativo delegado para manter o exemplo simples.
#import "MyAppDelegate.h" @implementation MyAppDelegate @synthesize webView, scriptObject; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [webView setFrameLoadDelegate:self]; [webView setMainFrameURL:@"http://blog.jerodsanto.net"]; }
O método delegado para empregar é o -webView:didFinishLoadForFrame. Ele será chamado depois que cada frame no WebView for carregado. Como você só deseja configurar a bridge uma vez, verifique que o frame que acabou de ser carregado é chamado de “_top” (saiba mais).
- (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { if (frame == [frame findFrameNamed:@"_top"]) { // bridge code will go here } }
Uma vez dentro do argumento if, o ambiente do script é totalmente inicializado. Obtenha uma referência para o objeto de script:
scriptObject = [sender windowScriptObject];
Agora, registre seu objeto de modo que os métodos possam ser chamados a partir do JavaScript:
[scriptObject setValue:self forKey:@"MyApp"];
Nesse ponto, a instância do MyAppDelegate é acessível para o JavaScript e como window.MyApp e os métodos podem ser chamados a partir do JavaScript! Bem, ainda não…
Por razões de segurança, você tem que fazer opt-in para que os seus métodos de Objective-C sejam executáveis a partir do JavaScript. Primeiro, adicione o método que será chamado. Ele vai simplesmente pegar a string de mensagem do JavaScript e passar para NSLog.
- (void)consoleLog:(NSString *)aMessage { NSLog(@"JSLog: %@", aMessage); }
Ok, o método é definido. Agora, ele tem que ficar explicitamente disponível para o JavaScript implementando um método de classe chamado isSelectorExcludedFromWebScript:
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { if (aSelector == @selector(consoleLog:)) { return NO; } return YES; }
Tudo o que sobrou agora foi definir/substituir o objeto window.console que vai preencher a sua função log para o método do objeto do console exposto em MyAppDelegate CONSOLELOG:
[scriptObject evaluateWebScript:@"console = { log: function(msg) { MyApp.consoleLog_(msg); } }"];
Isso é tudo o que tem para fazer! A seguir, o exemplo do MyAppDelegate.m na íntegra:
#import "MyAppDelegate.h" @implementation MyAppDelegate @synthesize webView, scriptObject; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { [webView setFrameLoadDelegate:self]; [webView setMainFrameURL:@"http://blog.jerodsanto.net"]; } - (void)consoleLog:(NSString *)aMessage { NSLog(@"JSLog: %@", aMessage); } + (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { if (aSelector == @selector(consoleLog:)) { return NO; } return YES; } - (void)webView:(WebView *)sender didFinishLoadForFrame:(WebFrame *)frame { if (frame == [frame findFrameNamed:@"_top"]) { scriptObject = [sender windowScriptObject]; [scriptObject setValue:self forKey:@"MyApp"]; [scriptObject evaluateWebScript:@"console = { log: function(msg) { MyApp.consoleLog_(msg); } }"]; } } @end
Uma vez que você tem isso configurado, você pode utilizar console.logs o quanto quiser e obter o feedback de que precisa lá no Xcode.
Espero que isso ajude!
***
Texto original disponível em http://blog.jerodsanto.net/2010/12/bridging-the-gap-between-javascripts-console-log-and-cocoas-nslog/