Phonegap handleOpenURL not called on app launch (iOS) - javascript

Getting alert message when app is open in background.When I close the app from background when I launch the app it don't give me alert message. handleOpenURL not able to invoke in JavaScript when app is first time launch. Following are code
didFinishLaunchingWithOptions Code
NSURL* url = [launchOptions objectForKey:UIApplicationLaunchOptionsURLKey];
NSString* invokeString = nil;
if (url) {
invokeString = [url absoluteString];
NSLog(#"iPaperReeder launchOptions = %#", url);
}
self.viewController.invokeString = invokeString;
AppDelgate.m
if (!url) { return NO; }
NSString* jsString = [NSString stringWithFormat:#"window.setTimeout(function(){ handleOpenURL(\"%#\"); }, 1)", url];
[self.viewController.webView stringByEvaluatingJavaScriptFromString:jsString];
// all plugins will get the notification, and their handlers will be called
[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:CDVPluginHandleOpenURLNotification object:url]];
return YES;
It should output to this javascript function:
function handleOpenURL(url) {
alert('invoke: ' + url);
}
Please help me.

I think you are not doing right, Please follow my code as below:
You can easily sort out this problem.
In "CDVHandleOpenURL.m" file you need to change code as below :
NSString* jsString = [NSString stringWithFormat:#"document.addEventListener('deviceready',function(){if (typeof handleOpenURL === 'function') { handleOpenURL(\"%#\");}});", url];
To
NSString* jsString = [NSString stringWithFormat:#"if (typeof handleOpenURL === 'function') { handleOpenURL(\"%#\");} else { window._savedOpenURL = \"%#\"; }", url, url];
this will work perfectly.
Best of luck

Related

How to close WKWebView via javascript on the site?

I have a finished application. I can not implement the closure of VKWebView from registration at the end of registration. Since I have not encountered this in the lens - p.
WKWebViewConfiguration *theConfiguration = [[WKWebViewConfiguration alloc] init];
WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.frame configuration:theConfiguration];
webView.navigationDelegate = self;
webView.customUserAgent = "Cron";
[self.view addSubview:webView];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"https://myUrl"]];
[webView loadRequest:request];
in order to interact with the WKWebView, you can use the javascript functions for them.
After the WKWebView has finished loading (via delegate for example), yo can use the evaluateJavascript functions, to trigger any event in the web or in your iOS code.
For example:
[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);
}
}];
The javascript function is:
var foo = 1; foo + 1;
There you can set any javascript function from objective-c side, or call any function in the web side.
For example, if you have a javascript function in your web site called:
func closeWeb()
You have to set the function like this:
[self.webview evaluateJavaScript:#"closeWeb();" completionHandler:^(id result, NSError *error) {
if (error == nil)
// Code for success
} else {
NSLog(#"evaluateJavaScript error : %#", error.localizedDescription);
}
}];

how to pass data from javascript to IOS Objective-c in webview?

I have a web app which i am using as Web-view app for android and IOS app.I need to pass user data to the web-view app which i achieved for android but i have no knowledge on how to do it for IOS web-view.
I have my IOS code in x-code which is in objective-c and i need to send data from frontend javascript to Objective-c, which can access it.
below is my javascript code
var myAppName = 'myfakeappname';
var myActionType = 'myJavascriptActionType';
var myActionParameters = {}; // put extra info into a dict if you need it
// (separating the actionType from parameters makes it easier to parse in ObjC.)
var jsonString = (JSON.stringify(myActionParameters));
var escapedJsonParameters = escape(jsonString);
var url = myAppName + '://' + myActionType + "#" + escapedJsonParameters;
document.location.href = url;
below is my code from Viewcontroller.m
#import "ViewController.h"
#import <WebKit/WebKit.h>
#interface ViewController ()<WKNavigationDelegate>
#property (weak, nonatomic) IBOutlet WKWebView *vwWeb;
#property (weak, nonatomic) IBOutlet UIView *vwLoading;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.vwWeb.navigationDelegate = self;
NSURL *nsurl=[NSURL URLWithString:#"http://localhost:3000"];
NSURLRequest *nsrequest=[NSURLRequest requestWithURL:nsurl];
[self.vwWeb loadRequest:nsrequest];
// Do any additional setup after loading the view, typically from a nib.
}
-(void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
self.vwLoading.hidden = YES;
}
//below is the code i copy pasted from various resourses i found from forums
- (BOOL)webView:( WKWebView *)webView
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
// these need to match the values defined in your JavaScript
NSString *myAppScheme = #"myfakeappname";
NSString *myActionType = #"myJavascriptActionType";
// ignore legit webview requests so they load normally
if (![request.URL.scheme isEqualToString:myAppScheme]) {
return YES;
}
// get the action from the path
NSString *actionType = request.URL.host;
// deserialize the request JSON
NSString *jsonDictString = [request.URL.fragment stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
NSLog(#"Hello, World!");
// look at the actionType and do whatever you want here
if ([actionType isEqualToString:myActionType]) {
NSLog(#"Missing function name");
// do something in response to your javascript action
// if you used an action parameters dict, deserialize and inspect it here
}
// make sure to return NO so that your webview doesn't try to load your made-up URL
return NO;
}
#end
Any help or suggestion will be greatly appreciated.
If your parameters is in the url, extract it by this property navigationAction.request.url in this function to get the params:
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: #escaping (WKNavigationActionPolicy) -> Void)
Exactly like you started:
In JavaScript the buttons/links that need to trigger something on iOS app should do: window.location="myappname://func1";
Javascript should also have a accessible method (ie should be accessible from browser console for example) myobject.jsfunc()
On iOS WebView you intercept requests with myappname:// URL, something like:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
NSURL *URL = [request URL];
NSString *requestStr = [[request URL] absoluteString]; //Get the URL
//NSLog(#"Native method call '%#'", requestStr);
if ([requestStr hasPrefix:#"openpanzer"]) {
//The [URL host] is the next part of the link so we can use that like a selector
NSString *selectorName = [URL host];
id data = nil;
NSMutableArray *parameters = [NSMutableArray array];
if ( ![[URL path] isEqualToString:#""] )
{
selectorName = [NSString stringWithFormat:#"%#:", selectorName];
parameters = [NSMutableArray arrayWithArray: [[URL path] componentsSeparatedByString:#"/"] ];
[parameters removeObjectAtIndex:0]; //first object is just a slash "/"
if ( [parameters count] == 0 ) {
data = nil;
NSLog(#"NIL parameter call");
}
else if ( [parameters count] == 1 ) {
data = [parameters objectAtIndex:0];
NSLog(#"Single parameter call %#", data);
}
else {
data = parameters;
}
}
SEL method = NSSelectorFromString( selectorName );
if ([nativeGlue respondsToSelector:method]) {
if (data == nil)
[nativeGlue performSelector:method];
else
[nativeGlue performSelector:method withObject:data];
}
else {
NSLog(#"Can't find native method '%#' with param '%#'", selectorName, data);
}
return NO;
}
return YES;
}
On iOS (WebViewDelegate or AppDelegate) define your func1 ie: - (void) func1
If you need to send back results to your javascript app use:
[webView callJSFunction: jsFunc];

Compress image plugin for ios in phonegap

I am new to ios and building compress image plugin in ios for phonegap.I am not getting how to call the compress image method in my javascript. My code is, Plugin.h file
- (void) cordovaCompressimg:(CDVInvokedUrlCommand *)command;
Plugin.m file
- (void) cordovaCompressimg:(CDVInvokedUrlCommand *)command
{
UIImage *sourceImage ;
NSString *compimg =[self UIImageToBaseSixtyFour];
CDVPluginResult *pluginResult = [ CDVPluginResult
resultWithStatus : CDVCommandStatus_OK
NSData : compimg
];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
-(NSData *)UIImageToBaseSixtyFour
{
UIImage *sourceImage ;
NSData *imageData = UIImageJPEGRepresentation(sourceImage, 1.0);
NSString *base64 = [NSString stringWithFormat:#"%#",[UIImageJPEGRepresentation(sourceImage, 0.95) base64EncodedString]];
return base64;
}
plugin.js file
window.showimg = function(cimg, callback) {
cordova.exec(callback, function(err) {
callback('Nothing to echo.');
}, "PhonegapPlugin", "cordovaGetCurrentDate", [cimg]);
};
my calling function in index.html is,
function showCompressimg() {
window.showimg("", function(echoValue)
{
alert(echoValue);
});
}
The plugin is getting called but with empty image.The out put comes as null.The source image is not getting passed.Can anybody help me please,
Thanks in advance
You are passing the source image correctly, but you are not retrieving the image passed in your argument in objective-c code. Whenever you pass any argument via plugin Javascript, those arguments are stored in CDVInvokedUrlCommand instance command. So you can modify your code like this -
- (void) cordovaCompressimg:(CDVInvokedUrlCommand *)command
{
//Arguments are passed in an Array. Source Image is present at Index 0
UIImage *sourceImage = [command argumentAtIndex:0];
NSString *compimg =[self UIImageToBaseSixtyFour:sourceImage];
CDVPluginResult *pluginResult = [ CDVPluginResult
resultWithStatus : CDVCommandStatus_OK
NSData : compimg
];
[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
}
-(NSData *)UIImageToBaseSixtyFour:(UIImage *)img
{
//UIImage *sourceImage ;
NSData *imageData = UIImageJPEGRepresentation(img, 1.0);
NSString *base64 = [NSString stringWithFormat:#"%#",[UIImageJPEGRepresentation(img, 0.95) base64EncodedString]];
return base64;
}

How can i monitor requests on WKWebview?

How can i monitor requests on WKWebview?
I'v tried using NSURLprotocol (canInitWithRequest) but it won't monitor ajax requests (XHR), only navigation requests(document requests)
Finally I solved it
Since I don't have control over the web view content, I injected to the WKWebview a java script that include a jQuery AJAX request listener.
When the listener catches a request it sends the native app the request body in the method:
webkit.messageHandlers.callbackHandler.postMessage(data);
The native app catches the message in a delegate called:
(void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message
and perform the corresponding actions
here is the relevant code:
ajaxHandler.js -
//Every time an Ajax call is being invoked the listener will recognize it and will call the native app with the request details
$( document ).ajaxSend(function( event, request, settings ) {
callNativeApp (settings.data);
});
function callNativeApp (data) {
try {
webkit.messageHandlers.callbackHandler.postMessage(data);
}
catch(err) {
console.log('The native context does not exist yet');
}
}
My ViewController delegate are:
#interface BrowserViewController : UIViewController <UIWebViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIWebViewDelegate>
And in my viewDidLoad(), I'm creating a WKWebView:
WKWebViewConfiguration *configuration = [[WKWebViewConfiguration alloc]init];
[self addUserScriptToUserContentController:configuration.userContentController];
appWebView = [[WKWebView alloc]initWithFrame:self.view.frame configuration:configuration];
appWebView.UIDelegate = self;
appWebView.navigationDelegate = self;
[appWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString: #"http://#############"]]];
Here is the addUserScriptToUserContentController:
- (void) addUserScriptToUserContentController:(WKUserContentController *) userContentController{
NSString *jsHandler = [NSString stringWithContentsOfURL:[[NSBundle mainBundle]URLForResource:#"ajaxHandler" withExtension:#"js"] encoding:NSUTF8StringEncoding error:NULL];
WKUserScript *ajaxHandler = [[WKUserScript alloc]initWithSource:jsHandler injectionTime:WKUserScriptInjectionTimeAtDocumentEnd forMainFrameOnly:NO];
[userContentController addScriptMessageHandler:self name:#"callbackHandler"];
[userContentController addUserScript:ajaxHandler];
}
#Benzi Heler answer is great, but it uses jQuery which seems like is not working in WKWebView anymore, so I have found solution without using jQuery.
Here is ViewController implementation that lets you be notified every AJAX request is completed in WKWebView:
import UIKit
import WebKit
class WebViewController: UIViewController {
private var wkWebView: WKWebView!
private let handler = "handler"
override func viewDidLoad() {
super.viewDidLoad()
let config = WKWebViewConfiguration()
let userScript = WKUserScript(source: getScript(), injectionTime: .atDocumentStart, forMainFrameOnly: false)
config.userContentController.addUserScript(userScript)
config.userContentController.add(self, name: handler)
wkWebView = WKWebView(frame: view.bounds, configuration: config)
view.addSubview(wkWebView)
if let url = URL(string: "YOUR AJAX WEBSITE") {
wkWebView.load(URLRequest(url: url))
} else {
print("Wrong URL!")
}
}
private func getScript() -> String {
if let filepath = Bundle.main.path(forResource: "script", ofType: "js") {
do {
return try String(contentsOfFile: filepath)
} catch {
print(error)
}
} else {
print("script.js not found!")
}
return ""
}
}
extension WebViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if let dict = message.body as? Dictionary<String, AnyObject>, let status = dict["status"] as? Int, let responseUrl = dict["responseURL"] as? String {
print(status)
print(responseUrl)
}
}
}
Pretty standard implementation. There is a WKWebView created programmatically. There is injected script that is loaded from script.js file.
And the most important part is script.js file:
var open = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
this.addEventListener("load", function() {
var message = {"status" : this.status, "responseURL" : this.responseURL}
webkit.messageHandlers.handler.postMessage(message);
});
open.apply(this, arguments);
};
userContentController delegate method will be called every time there is AJAX request loaded. I'm passing there status and responseURL, because this was what I needed in my case, but you can also get more informations about request. Here is the list of all properties and methods available:
https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest
My solution is inspired by this answer written by #John Culviner:
https://stackoverflow.com/a/27363569/3448282
If you have control of the content inside the WkWebView you can send messages to your native app using window.webkit.messageHandlers whenever you make an ajax request, which will be received as a WKScriptMessage that can be processed by whatever you've designated as your WKScriptMessageHandler. The messages can contain whatever information you wish, and will be automatically converted into native objects/values in your Objective-C or Swift code.
If you don't have control over the content you can still do this by injecting your own JavaScript via a WKUserScript to track ajax requests and send back messages using the method stated above.
You can use this to respond to requests from the WKWebView. It works similar to UIWebView.
- (void)webView:(WKWebView *)webView2 decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
if (navigationAction.navigationType == WKNavigationTypeLinkActivated) {
NSString *url = [navigationAction.request.URL absoluteString];
// Handle URL request internally
}
decisionHandler(WKNavigationActionPolicyAllow); // Will continue processing request
decisionHandler(WKNavigationActionPolicyCancel); // Cancels request
}

Javascript console.log() in an iOS UIWebView

When writing a iPhone / iPad app with a UIWebView, the console isn't visible.
this excellent answer shows how to trap errors, but I would like to use the console.log() as well.
After consulting with an esteemed colleague today he alerted me to the Safari Developer Toolkit, and how this can be connected to UIWebViews in the iOS Simulator for console output (and debugging!).
Steps:
Open Safari Preferences -> "Advanced" tab -> enable checkbox "Show Develop menu in menu bar"
Start app with UIWebView in iOS Simulator
Safari -> Develop -> i(Pad/Pod) Simulator -> [the name of your UIWebView file]
You can now drop complex (in my case, flot) Javascript and other stuff into UIWebViews and debug at will.
EDIT: As pointed out by #Joshua J McKinnon this strategy also works when debugging UIWebViews on a device. Simply enable Web Inspector on your device settings: Settings->Safari->Advanced->Web Inspector (cheers #Jeremy Wiebe)
UPDATE: WKWebView is supported too
I have a solution to log, using javascript, to the apps debug console.
It's a bit crude, but it works.
First, we define the console.log() function in javascript, which opens and immediately removes an iframe with a ios-log: url.
// Debug
console = new Object();
console.log = function(log) {
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "ios-log:#iOS#" + log);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
Now we have to catch this URL in the UIWebViewDelegate in the iOS app using the shouldStartLoadWithRequest function.
- (BOOL)webView:(UIWebView *)webView2
shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType {
NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
//NSLog(requestString);
if ([requestString hasPrefix:#"ios-log:"]) {
NSString* logString = [[requestString componentsSeparatedByString:#":#iOS#"] objectAtIndex:1];
NSLog(#"UIWebView console: %#", logString);
return NO;
}
return YES;
}
Here's the Swift solution:
(It's a bit of a hack to get the context)
You create the UIWebView.
Get the internal context and override the console.log() javascript function.
self.webView = UIWebView()
self.webView.delegate = self
let context = self.webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") as! JSContext
let logFunction : #convention(block) (String) -> Void =
{
(msg: String) in
NSLog("Console: %#", msg)
}
context.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, AnyObject.self),
forKeyedSubscript: "log")
Starting from iOS7, you can use native Javascript bridge. Something as simple as following
#import <JavaScriptCore/JavaScriptCore.h>
JSContext *ctx = [webview valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
ctx[#"console"][#"log"] = ^(JSValue * msg) {
NSLog(#"JavaScript %# log message: %#", [JSContext currentContext], msg);
};
NativeBridge is very helpful for communicating from a UIWebView to Objective-C. You can use it to pass console logs and call Objective-C functions.
https://github.com/ochameau/NativeBridge
console = new Object();
console.log = function(log) {
NativeBridge.call("logToConsole", [log]);
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;
window.onerror = function(error, url, line) {
console.log('ERROR: '+error+' URL:'+url+' L:'+line);
};
The advantage of this technique is that things like newlines in log messages are preserved.
Swift 5
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript("your javascript string") { (value, error) in
if let errorMessage = (error! as NSError).userInfo["WKJavaScriptExceptionMessage"] as? String {
print(errorMessage)
}
}
}
Tried Leslie Godwin's fix but was getting this error:
'objectForKeyedSubscript' is unavailable: use subscripting
For Swift 2.2, here's what worked for me:
You will need to import JavaScriptCore for this code to compile:
import JavaScriptCore
if let context = webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: #convention(block) String -> Void = { message in
print("javascript_log: " + message)
}
context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}
Then in your javascript code, calling console.log("_your_log_") will print in Xcode console.
Better yet, add this code as an extension to UIWebView:
import JavaScriptCore
extension UIWebView {
public func hijackConsoleLog() {
if let context = valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
let consoleLog: #convention(block) String -> Void = { message in
print("javascript_log: " + message)
}
context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}
}
}
And then call this method during your UIWebView initialization step:
let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()

Categories