| 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);
}