Os WebViews do Cocoa fornecem um objeto de script pelo qual você pode executar JavaScript a partir do Objective-C e Objective-C do JavaScript.
Infelizmente, obter o feedback do JavaScript executado dentro de um WebView não é totalmente simples. Exceções são convertidas em undefineds (para saber mais sobre isso, clique aqui) e você pode apenas obter de volta um único valor de retorno para usar para depuração.
Não seria legal se você pudesse apenas continuar usando chamadas console.log() de dentro do JavaScript como você está acostumado e ter o resultado exibido no console depurador do Xcode? Boas notícias, povo. 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 a ser empregado é -webView:didFinishLoadForFrame. Ele será ser chamado depois que cada frame no WebView for carregado. Como você só quer criar a ponte 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 de script é totalmente inicializado. Obtenha uma referência para o objeto de script:
scriptObject = [sender windowScriptObject];
Agora, registre seu objeto, de modo que os seus métodos possam ser chamados a partir do JavaScript:
[scriptObject setValue:self forKey:@"MyApp"];
Nesse ponto, a instância do MyAppDelegate está acessível para o JavaScript como window.MyApp e seus métodos podem ser chamados do JavaScript! Bem, ainda não…
Por razões de segurança, você precisa dar um opt-in em seus métodos do Objective-C para serem 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 passá-la para NSLog.
- (void)consoleLog:(NSString *)aMessage { NSLog(@"JSLog: %@", aMessage); }
Okay, o método está definido. Agora ele tem que ser disponibilizado explicitamente para JavaScript por implementação de um método de classe chamado isSelectorExcludedFromWebScript:
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector { if (aSelector == @selector(consoleLog:)) { return NO; } return YES; }
Tudo o que resta agora é definir/substituir o objeto window.console que irá preencher a sua função log para o método do objeto exposto em MyAppDelegate do consoleLog:
[scriptObject evaluateWebScript:@"console = { log: function(msg) { MyApp.consoleLog_(msg); } }"];
Isso é tudo o que tem para ser feito! Aqui está o exemplo 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
Assim que tiver essa configuração, você pode usar console.logs do jeito que desejar e obter o feedback que você 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/