| Customizations and Extensions / Use front calls | |
Call Objective-C methods from Genero application for the GWC hybrid mode for iOS.
The principle is to use a custom URL call in the javascript framework of Genero application, with a custom URL Scheme. When the native application traps this call in the shouldStartLoadWithRequest method (WebViewController.m file), you can trigger custom actions.
See Implementing Custom URL Schemes in the iOS documentation.
#pragma mark UIWebViewDelegate
// This selector is called when something is loaded in our webview
// By something I don't mean anything but just "some" :
// - main html document
// - sub iframes document
//
- (BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[request URL] absoluteString];
NSLog(@"request : %@",requestString);
if ([requestString hasPrefix:@"ghc-frame:"]) {
NSArray *components = [requestString componentsSeparatedByString:@":"];
NSString *function = (NSString*)[components objectAtIndex:1];
int callbackId = [((NSString*)[components objectAtIndex:2]) intValue];
NSString *argsAsString = [(NSString*)[components objectAtIndex:3]
stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
if ([NSJSONSerialization isValidJSONObject:components]) {
NSLog(@"isValid");
} else {
NSLog(@"Is NOT VALID");
}
NSLog(@"components %@", components);
NSArray *args = [NSArray arrayWithObject:argsAsString];
[self handleCall:function callbackId:callbackId args:args];
return NO;
}
return YES;
}
#pragma mark gwc ghc bridge
// Call this function when you have results to send back to javascript callbacks
// callbackId : int comes from handleCall function
// args: list of objects to send to the javascript callback
- (void)returnResult:(int)callbackId args:(id)arg, ...;
{
if (callbackId==0) return;
va_list argsList;
NSMutableArray *resultArray = [[NSMutableArray alloc] init];
if(arg != nil){
[resultArray addObject:arg];
va_start(argsList, arg);
while((arg = va_arg(argsList, id)) != nil)
[resultArray addObject:arg];
va_end(argsList);
}
NSString *resultArrayString = [resultArray objectAtIndex:0];
// We need to perform selector with afterDelay 0
// in order to avoid weird recursion stop
// when calling NativeBridge in a recursion more
// than 200 times
[self performSelector:@selector(returnResultAfterDelay:)
withObject:[NSString
stringWithFormat:@"gwc.ghc.resultForCallback('%d',['%@'])",
callbackId,resultArrayString]
afterDelay:0];
}
-(void)returnResultAfterDelay:(NSString*)str {
// Now perform this selector with waitUntilDone:NO
// in order to get a huge speed boost!
NSLog(@"str : %@", str);
[self performSelectorOnMainThread:
@selector(stringByEvaluatingJavaScriptFromString:)
withObject:str waitUntilDone:NO];
}
// Implements all you native function in this one,
// by matching 'functionName' and parsing 'args'
// Use 'callbackId' with 'returnResult' selector when
// you get some results to send back to javascript
- (void)handleCall:(NSString*)functionName
callbackId:(int)callbackId args:(NSArray*)args
{
if ([functionName isEqualToString:@"setKeepAliveURL"]) {
if ([args count]!=1) {
NSLog(@"setKeepAliveURL wait exactly 1 arguments!");
return;
}
NSMutableString *kaurl = (NSMutableString*)[args objectAtIndex:0];
[kaurl replaceOccurrencesOfString:@"\"" withString:@""
options:NSCaseInsensitiveSearch
range:NSMakeRange(0, [kaurl length])];
// we send keepAliveUrl to delegate object
ghcAppDelegate *delegate =
(ghcAppDelegate *)[[UIApplication sharedApplication] delegate];
[delegate setKeepAliveURL:kaurl];
} else if ([functionName isEqualToString:@"nativeYesNo"]) {
if ([args count]!=1) {
NSLog(@"nativeYesNo wait exactly one argument!");
return;
}
NSMutableString *message = (NSMutableString*)[args objectAtIndex:0];
[message replaceOccurrencesOfString:@"\"" withString:@""
options:NSCaseInsensitiveSearch range:NSMakeRange(0, [message length])];
alertCallbackId = callbackId;
UIAlertView *alert=[[UIAlertView alloc]
initWithTitle:nil message:message delegate:self
cancelButtonTitle:@"Cancel" otherButtonTitles:@"Ok", nil];
[alert show];
} else {
NSLog(@"Unimplemented method '%@'",functionName);
}
}
// AlertView that show how to return asynchronous results
- (void)alertView:(UIAlertView *)alertView
clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (!alertCallbackId) return;
NSLog(@"prompt result : %d",buttonIndex);
NSString * result = buttonIndex==1?@"YES":@"NO";
NSLog(@"result : %@", result);
[self returnResult:alertCallbackId args:result,nil];
alertCallbackId = 0;
}
gwc.ghc = {
callbacksCount : 1,
callbacks : {},
// Automatically called by native layer when a result is available
resultForCallback : function resultForCallback(callbackId, resultArray) {
try {
var callback = gwc.ghc.callbacks[callbackId];
if (!callback) return;
callback.apply(null,resultArray);
} catch(e) {alert(e)}
},
// Use this in javascript to request native objective-c code
// functionName
// args : array of arguments
// callback : function with n-arguments that is going to be
// called when the native code
returned
invoke : function invoke (functionName, args, callback) {
var hasCallback = callback && typeof callback == "function";
var callbackId = hasCallback ? gwc.ghc.callbacksCount++ : 0;
if (hasCallback) {
gwc.ghc.callbacks[callbackId] = callback;
}
if (isAndroid) {
GHCAndroid[functionName](encodeURIComponent(JSON.stringify(args)),
callbackId);
} else {
// GHC-ios
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "ghc-frame:" + functionName + ":" + callbackId+
":" + encodeURIComponent(JSON.stringify(args)));
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
},
callApi : function (functionName, args) {
// this fails ->
//this.api[functionName](JSON.parse(args));
this.api[functionName](args);
},
api : {}
}
gwc.ghc.api.getKeepAliveURL = function () {
var url = gwc.api.keepAliveURL();
var json = {};
json.url = url;
gwc.ghc.invoke("setKeepAliveURL", url);
}