Git Product home page Git Product logo

jockeyjs's Introduction

JockeyJS

JockeyJS is a dual-iOS and Android library that facilitates two-way communication between native applications and JavaScript apps running inside them. It also supports communication between iframes running inside a webview.

Setup - iOS

JockeyJS will help your iOS app communicate with a JavaScript application running inside a UIWebview.

  1. Download the latest JockeyJS into your iOS project directory.
  2. Add JockeyJS/includes/Jockey.m and Jockey.h to your project by right clicking inside XCode's Project Navigator and selecting "Add Files to <YourProject>"
  3. In your web app, make sure to include JockeyJS/js/jockey.js as a script tag.
  4. Last, set your ViewController as the delegate of your UIWebView (JockeyViewController in the example code), then add the following method to your ViewController's .m file:
-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
	return [Jockey webView:webView withUrl:[request URL]];
}

Setup - Android

JockeyJS will help your Android app communicate with a JavaScript application running inside a WebView.

  1. Download the latest JockeyJS.Android project.
  2. Add a library reference to JockeyJS.Android in your Android application (in Eclipse this is done through [Right-click Project]->Properties->Android->Add..)
  3. In your web app make sure to include JockeyJS/js/jockey.js as a script tag.
  4. Add Jockey into your app.

There are two ways you can use Jockey in your Android app.

The first is if you plan to use Jockey in only one activity or want to keep instances seperate between activities

//Declare an instance of Jockey
Jockey jockey;

//The WebView that we will be using, assumed to be instantiated either through findViewById or some method of injection.
WebView webView;

WebViewClient myWebViewClient;

@Override
protected void onStart() {
	super.onStart();

	//Get the default JockeyImpl
	jockey = JockeyImpl.getDefault();

	//Configure your webView to be used with Jockey
	jockey.configure(webView);

	//Pass Jockey your custom WebViewClient
	//Notice we can do this even after our webView has been configured.
	jockey.setWebViewClient(myWebViewClient)

	//Set some event handlers
	setJockeyEvents();

	//Load your webPage
	webView.loadUrl("file:///your.url.com");
}

The second option is to make use of the JockeyService, this is useful if you would like to share event handlers across several activities or applications, or you just want to make sure that Jockey's lifecycle is explicitly bound to your application's lifecycle.

Here is an example of how to bind to the JockeyService locally, please see the Android docs for more detail on this process.

####Important If you will be using Jockey as a Service make sure to include it in your applicaiton manifest, otherwise you will not be able to bind it.

//First we declare the members involved in using Jockey

//A WebView to interact with
private WebView webView;

//Our instance of the Jockey interface
private Jockey jockey;

//A helper for binding services
private boolean _bound;

//A service connection for making use of the JockeyService
private ServiceConnection _connection = new ServiceConnection() {
	@Override
	public void onServiceDisconnected(ComponentName name) {
		_bound = false;
	}

	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		JockeyBinder binder = (JockeyBinder) service;

		//Retrieves the instance of the JockeyService from the binder
		jockey = binder.getService();

		//This will setup the WebView to enable JavaScript execution and provide a custom JockeyWebViewClient
		jockey.configure(webView);

		//Make Jockey start listening for events
		setJockeyEvents();

		_bound = true;

		//Redirect the WebView to your webpage.
		webView.loadUrl("file:///android_assets/index.html");
	}

}

///....Other member variables....////


//Then we bind the JockeyService to our activity through a helper function in our onStart method
@Override
protected void onStart() {
	super.onStart();
	JockeyService.bind(this, _connection);
}

//In order to bind this with the Android lifecycle we need to make sure that the service also shuts down at the appropriate time.
@Override
protected void onStop() {
	super.onStop();
	if (_bound) {
		JockeyService.unbind(this, _connection);
	}
}

Setup - Other Platform

JockeyJS will help your app communicate with a JavaScript application running inside an Iframe.

  1. Download the latest JockeyJS into your app's project directory.
  2. In your web app, make sure to include JockeyJS/js/jockey.js as a script tag.
  3. Include an iframe with the application running inside your app.
  4. Configure Jockey with the target iframe your app will communicate with (this part is optional):
Jockey.restrictIframeDispatcher('http://www.example.com', $('iframe')[0].contentWindow)

You're all set! Now you can start passing events.

Sending events from app to JavaScript

JockeyJS allows you to not only send events to the JavaScript application, but you can also receive a callback in the form of a block when all JavaScript listeners have finished executing. There are two methods available:

#####iOS

// Send an event to JavaScript, passing a payload.
// payload can be an NSDictionary or NSArray, or anything that is serializable to JSON.
// It can be nil.
[Jockey send:@"event-name" withPayload:payload toWebView:webView];

// If you want to send an event and also execute code within the iOS app when all
// JavaScript listeners have finished processing.
[Jockey send:@"event-name" withPayload:payload toWebView:webView perform:^{
  // Respond to callback.
}];

#####Android

// Send an event to JavaScript, passing a payload
jockey.send("event-name", webView, payload);

//With a callback to execute after all listeners have finished
jockey.send("event-name", webView, payload, new JockeyCallback() {
	@Override
	public void call() {
		//Your execution code
	}
});

#####Other platforms

Using iframes communication is identical for both the sender and receiver (parent app and app running inside the iframe). The following examples are the same as the ones of sending events from Javascript to the app:

// Send an event to internal Iframe, passing a payload

// Send an event to iframe app.
Jockey.send("event-name");

// Send an event to iframe app, passing an optional payload.
Jockey.send("event-name", {
  key: "value"
});

// Send an event to iframe app, pass an optional payload, and catch the callback when all the
// iframe app listeners have finished processing.
Jockey.send("event-name", {
  key: "value"
}, function() {
  alert("Iframe app has finished processing!");
});

Receiving events from app in JavaScript

Event listeners in Jockey are modeled after JQuery's event listeners (but far less featureful). To receive the above events in JavaScript, simply add the following to your JavaScript application:

// Listen for an event from iOS and log the payload.
Jockey.on("event-name", function(payload) {
  console.log(payload);
});

You can also pass a slightly different function to on() in cases where your listener fires off other events and you don't want to send a callback to iOS until those events are completed. e.g.,

// Listen for an event from iOS, but don't notify iOS we've completed processing
// until an asynchronous function has finished (in this case a timeout).
Jockey.on("event-name", function(payload, complete) {
  // Example of event'ed handler.
  setTimeout(function() {
    alert("Timeout over!");
    complete();
  }, 1000);
});
// Stop listening to an event from ios
Jockey.off("event-name");

Sending events from JavaScript to app

Similar to iOS above, Jockey's JavaScript library lets you pass events from your JavaScript application to your iOS app.

// Send an event to iOS.
Jockey.send("event-name");

// Send an event to iOS, passing an optional payload.
Jockey.send("event-name", {
  key: "value"
});

// Send an event to iOS, pass an optional payload, and catch the callback when all the
// iOS listeners have finished processing.
Jockey.send("event-name", {
  key: "value"
}, function() {
  alert("iOS has finished processing!");
});

Receiving events from JavaScript in app

#####iOS

Like JavaScript above, Jockey's iOS library has methods to easily help you listen for events sent from your JavaScript application:

// Listen for an event from JavaScript and log the payload.
[Jockey on:@"event-name" perform:^(NSDictionary *payload) {
  NSLog(@"payload = %@", payload);
}];

// Listen for an event from JavaScript, but don't notify the JavaScript that
// the listener has completed until an asynchronous function has finished.
[Jockey on:@"event-name" performAsync:^(NSDictionary *payload, void (^complete)()) {
  // Do something asynchronously, then call the complete() method when finished.
}];

// Stop listening for events from javascript, Jockey is a shared instance after first initialization
// If you're webview controller is initialized and deinitialized, this is useful.
[Jockey off:@"event-name"];

#####Android

//Listen for an event from JavaScript and log a message when we have receied it.
jockey.on("event-name", new JockeyHandler() {
	@Override
	protected void doPerform(Map<Object, Object> payload) {
		Log.d("jockey", "Things are happening");
	}
});

//Listen for an event from JavaScript, but don't notify the JavaScript that the listener has completed
//until an asynchronous function has finished
//Note: Because this method is executed in the background, if you want the method to interact with the UI thread
//it will need to use something like a android.os.Handler to post to the UI thread.
jockey.on("event-name", new JockeyAsyncHandler() {
	@Override
	protected void doPerform(Map<Object, Object> payload) {
		//Do something asynchronously
		//No need to called completed(), Jockey will take care of that for you!
	}
});


//We can even chain together several handlers so that they get processed in sequence.
//Here we also see an example of the NativeOS interface which allows us to chain some common
//system handlers to simulate native UI interactions.
jockey.on("event-name", nativeOS(this)
			.toast("Event occurred!")
			.vibrate(100), //Don't forget to grant permission
			new JockeyHandler() {
				@Override
				protected void doPerform(Map<Object, Object> payload) {
				}
			}
);

//...More Handlers


//If you would like to stop listening for a specific event
jockey.off("event-name");

//If you would like to stop listening to ALL events
jockey.clear();

#####Other platforms

//Listen for an event from JavaScript and log the payload.
Jockey.on("event-name", function(payload) {
  console.log(payload);
});

//If you would like to stop listening for a specific event
Jockey.off("event-name");

Security

#####iOS

You'll want to make sure your iOS app only responds to events sent from domains you control (for instance, if your UIWebView allows the user to navigate to other pages, you don't want those other pages to be able to communicate with or control your iOS app). To do this, simply add a check within the method you added to your ViewController during setup:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
    // Get current URL of the webview through Javascript.
    NSString *urlString = [_webView stringByEvaluatingJavaScriptFromString:@"window.location.href"];
    NSURL *currentURL = [NSURL URLWithString:urlString];

    NSString *host = [currentURL host];

    if ([host isEqualToString:@"mydomain.com") {
        return [Jockey webView:webView withUrl:[request URL]];
    }

    return TRUE;
}

#####Android

jockey.setValidationListener(new OnValidateListener() {
	@Override
	public boolean validate(String host) {
		return "mydomain.com".equals(host);
	}
});

#####Other platforms

For communication between iframes, jockey uses window.postMessage(). The default target domain is '*' and the default target frame is window.parent, meaning that the sender/receiver iframe is not verified. It is advised to always restrict the target iframe:

// Restricts the target domain and target frame
Jockey.restrictIframeDispatcher('http://www.example.com', $('iframe')[0].contentWindow)

Contributors

  • @tcoulter - original author (iOS only)
  • @paulpdaniels - Android support

jockeyjs's People

Contributors

andrewschaaf avatar douglasheriot avatar moriaklarna avatar paulpdaniels avatar shaunnez avatar t-evans avatar tcoulter avatar vially avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

jockeyjs's Issues

Callback with data

Hello -

I noticed when I send a message using the Jockey send method, there's no way to receive data back in the callback.

Is there a reason that this wasn't included? I.e., would it be possible to get the following with the callback function receiving a payload from the app?

Jockey.send("event-name", {
  key: "value"
}, function() {
  alert("iOS has finished processing!");
});

Undefined callback

I've searched the issues and couldn't find one, so...

I keep getting a TypeError: undefined is not a function (evaluating 'this.callbacks[t]()') from my minified Jockey file (triggerCallback:function(t){this.callbacks[t]()}). I suppose it relates to this, where for some reason id is undefined and/or points to a callback that doesn't exist:

        triggerCallback: function(id) {
            this.callbacks[id]();
        },

It happens on iOS (the user agent I got from Aibrake is Safari 12H321).

Did you have any error like this? Also, since I'm not much into Jockey code base, would it be ok to just wrap it into a try/catch?

Thanks!

Podfile integration

If you could do Podfile integration that would be cool, otherwise I might look into doing it myself just to clean up my own project.

Support for Crosswalk Webview

We are big fans of jockeyjs. For android we'd like to move to use Crosswalk: it enables the embedding of a recent chrome browser on older devices. https://crosswalk-project.org/

It seems some of the bindings are missing/different. Would this be something you'd be interested in? Or able to pull of easily?

Don't send Jockey events on mobile Safari (for debugging)

If you open a URL on mobile Safari (e.g. in the simulator, but NOT an embedded webView, actually in the mobile browser) that sends Jockey events, you'll see an error from the browser:

"Cannot Open Page - Safari cannot open the page because the address is invalid."

I believe this is because of the window's location is set to "jockey://..". It is sometimes convenient to debug using mobile Safari instead of OS X Safari or the embedded UIWebView.

One workaround would be to detect mobile Safari and for any Jockey.send() calls skip the default behavior + log to console.

Android Integration

A cursory glance at the android documentation suggests that there is a way to inject a java object directly into the javascript of an android WebView.

If I have some extra time I'll see if I can throw something together.

Use f.length instead of returning false?

Example from the README:

Jockey.on("event-name", function(payload, complete) {
  setTimeout(function() { complete(); }, 1000);

  // Note: You MUST return false in this case to tell Jockey you plan on calling
  // the complete function on your own once events have finished.
  return false;
});

The return false hassle can be avoided.

The Jockey code that calls this function literal can detect how many arguments it has (simply f.length).

You could have function(payload){} be treated as sync and function(payload, complete){} be treated as async.

Mocha uses this. (See its documentation's Sync and Async sections)

Problem listening to JS events from Android App

Hello,

First thanks for the library. Amazing job. Second able, I don't quite follow the issue here. So I tried your sample Android app and seems to be working correctly so I can send events from the JS and receive them in the Android app. But now when I take my website js which is loaded from a remote server, add the send method with the custom event and listen to it in the Android app, nothing gets trigger (In the iOS app seems to be working perfect). This happens with your Android sample app as well as my Android application.

Thanks

optional `off` argument to off a specific handler

Hi, thanks for this great bridge!

In some scenario, on js side an additional optional second argument of off to only unsubscribe specific handler could be really helpful.

function firstHandler(){
  console.log('I am on fire');
}
function secondHandler(){
  console.log('me too');
}

Jockey.on('myEvent', firstHandler);
Jockey.on('myEvent', secondHandler);
Jockey.off('myEvent', secondHandler);

// when event 'myEvent' comes:
// output is only 'I am on fire'

I'd happy to provide a PR if other ppl also like to have this feature

Composite Handlers

One possible enhancement is to provide an easy mechanism for creating composite or chained handlers. Ideally, several aspects of the parent OS could then be baked into event handling.

For instance Android could provide a fluent interface of sorts:

jockey.bind("click").to(nativeOS(this).vibrate(45), 
                                nativeOS(this).toast("Click event", Toast.LENGTH_SHORT), 
                                myCustomHandler);

I am not sure what the equivalent iOS code would look like.

嵌套调用jockey.on,出现函数被覆盖

var dataInit = function () {
GeoBOS.contacts.getContactsList(function( error, contactsList ){
if (error === null) {
//for (var i = 0; i < 10000; i++) {
$('#autoTestResultContactsList').text(contactsList.length);
//}
} else {
$('#autoTestResultContactsList').text('getContactsList failed: ' + JSON.stringify(error));
}
});

GeoBOS.im.getRecentChats(function(error, chatList){
        if(error === null){




            //for (var j = 0; j < 10000; j++) {
                $('#autoTestResultRecentChats').text(JSON.stringify(chatList));
            //}
        }else{
            $('#autoTestResultRecentChats').text("getRecentChats failed with " + selectedUserId + ":" + JSON.stringify(error));
        }

});

};

You can drop json2.js

UIWebView's JavaScript engine's stdlib includes JSON.stringify.

NSString *html = @"<html><head><script type=\"text/javascript\">alert(JSON.stringify({x:'\"'}));</script></head><body></body></html>";
NSURL *url = [NSURL URLWithString:@"http://localhost/"];
[self.webView loadHTMLString:html baseURL:url];

Publish jockyjs's js on npm or bower

Hey,

The app for our webview lives outside of our iOS app. It'd be nice if we could include Jockey.js with a dependency manager in our web app.

Podspec update -

Hi @tcoulter ,

it seems the old repository https://github.com/minsikzzang/jockeyjs got removed from github.

In the cocoapod spec repository it still points to the old version.
https://github.com/CocoaPods/Specs/blob/d0ec5a65e80656c8d78e12ff19f251df879e0bc2/Specs/0/f/6/JockeyJS/1.0.0/JockeyJS.podspec.json

I assumed just sending a pull request updating the spec would do the trick.
I see there is an update at https://github.com/tcoulter/jockeyjs/blob/master/JockeyJS.podspec

But simple updating the github reference isn't enough as it also looks for a tag 1.0.0 , and you github repo doesn't have any releases. Could you provide a 'release' and update the cocoapods spec?
I'm happy to help in case I can.

HTML dynamic image loading is not working in IOS Webview

Hi ,
I am using html files inside my native IOS app with jockeyjs. I am trying to load images from native app's storage, after loading the html page using jockeyjs. In this case I am pushing image names in json to html page. HTML files and images are in same directory.

HTML before Image Loading

<html>
<head>
<meta charset="UTF-8">
<script src="jquery.js"></script>
<script src="jockey.js"></script>
<script>
Jockey.on("load_image",function(CONTENT){
var img_src = CONTENT.image;
$(".image-div").append("<img src=\""+img_src+"\" />");
});
</script>
</head>
<body>
<div class="image-div"></div>
</body>
</html>

HTML after Image Loading

<html>
<head>
<meta charset="UTF-8">
<script src="jquery.js"></script>
<script src="jockey.js"></script>
<script>
Jockey.on("load_image",function(CONTENT){
var img_src = CONTENT.image;
$(".image-div").append("<img src=\""+img_src+"\" />");
});
</script>
</head>
<body>
<div class="image-div">
<img src="test_image.jpg" />
</div>
</body>
</html>

But the Image is not getting displayed on page.

TypeScript definition

Hello,

I want to use your library with Typescript. Could you write a definition file for it?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.