Merge branch 'release/v0.16.0'

This commit is contained in:
Ali Najafizadeh
2015-12-09 13:35:07 -05:00
64 changed files with 1510 additions and 873 deletions
+94 -93
View File
@@ -1,7 +1,7 @@
# React Native WebView Javascript Bridge
This project is inspired by [WebViewJavascriptBridge](https://github.com/marcuswestin/WebViewJavascriptBridge).
I have been testing and reading a lot of way to safely create a bridge between react-native and webview. I'm happy to announced that the wait is over and from **React-Native 0.16 and above**, the bridge is fully functional.
> In order for me to extend React-Native's WebView, I had to use `Category` feature objective-c, that would be the simplest and most elegant way by far.
## Installation
@@ -9,115 +9,116 @@ In order to use this extension, you have to do the following steps:
1. in your react-native project, run `npm install react-native-webview-bridge`
2. go to xcode's `Project Navigator` tab
<p align="center">
<img src ="https://raw.githubusercontent.com/alinz/react-native-webview-bridge/master/doc/assets/01.png" />
</p>
3. right click on `Libraries`
4. select `Add Files to ...` option
5. navigate to `node_modules/react-native-webview-bridge` and add `WebViewBridge` folder
6. clean compile to make sure your project can compile and build.
<p align="center">
<img src ="https://raw.githubusercontent.com/alinz/react-native-webview-bridge/master/doc/assets/02.png" />
</p>
5. navigate to `node_modules/react-native-webview-bridge/ios` and add `React-Native-Webview-Bridge.xcodeproj` folder
<p align="center">
<img src ="https://raw.githubusercontent.com/alinz/react-native-webview-bridge/master/doc/assets/03.png" />
</p>
6. on project `Project Navigator` tab, click on your project's name and select Target's name and from there click on `Build Phases`
<p align="center">
<img src ="https://raw.githubusercontent.com/alinz/react-native-webview-bridge/master/doc/assets/04.png" />
</p>
7. expand `Link Binary With Libraries` and click `+` sign to add a new one.
8. select `libReact-Native-Webviwe-Bridge.a` and click `Add` button.
<p align="center">
<img src ="https://raw.githubusercontent.com/alinz/react-native-webview-bridge/master/doc/assets/05.png" />
</p>
9. clean compile to make sure your project can compile and build.
## Usage
There is a script which will be injected by this extension to the first page that you load. In order for your webpage to get access to the injected script, you have to use the following function.
just import the module with one of your choices way:
** CommonJS style **
```js
function WebViewBridgeReady(cb) {
//checks whether WebViewBirdge exists in global scope.
if (window.WebViewBridge) {
cb(window.WebViewBridge);
return;
}
function handler() {
//remove the handler from listener since we don't need it anymore
document.removeEventListener('WebViewBridge', handler, false);
//pass the WebViewBridge object to the callback
cb(window.WebViewBridge);
}
//if WebViewBridge doesn't exist in global scope attach itself to document
//event system. Once the code is being injected by extension, the handler will
//be called.
document.addEventListener('WebViewBridge', handler, false);
}
```
so now, anywhere in your script in webpage, you can call
```js
WebViewBridgeReady(function (WebViewBridge) {
//at this time, you should be able to use the injected code here.
});
```
`WebViewBridge` exposes 2 methods, `send` and `onMessage`;
if you want to send a message to `React-Native` component, call the `send` method.
if you want to receive message from `React-Native`, attach a function to `onMessage`.
For Example:
```js
WebViewBridgeReady(function (WebViewBridge) {
WebViewBridge.onMessage = function(message) {
console.log('got a message from react-native', message);
};
//sending a message to react-native
WebViewBridge.send("Hello this is me calling from web page");
});
```
On React-Native side, you just have to load the `WebViewBridge` component.
```js
var React = require('react-native');
var WebViewBridge = require('react-native-webview-bridge');
```
Since `WebViewBridge` is extending `WebView` component, it behaves exactly as WebView.
What it means that `WebViewBridge` has all the methods and props of `WebView` component.
So here's an example of using `WebViewBridge`,
** ES6/ES2015 style **
```js
var React = require('react-native');
var WebViewBridge = require('react-native-webview-bridge');
import WebViewBridge from 'react-native-webview-bridge';
```
var {
Component
} = React;
`WebViewBridge` is an extension of `WebView`. It injects special script into any pages once it loads. Also it extends the functionality of `WebView` by adding 1 new method and 1 new props.
var WEBVIEW_REF = 'my_webview';
#### sendToBridge(message)
the message must be in string. because this is the only way to send data back and forth between native and webview.
class MyAwesomeView extends Component {
constructor(props) {
super(props);
}
#### onBridgeMessage
this is a prop that needs to be a function. it will be called once a message is received from webview. The type of received message is also in string.
## Bridge Script
bridge script is a special script which injects into all the webview. It automatically register a global variable called `WebViewBridge`. It has 2 optional methods to implement and one method to send message to native side.
#### send(message)
this method sends a message to native side. the message must be in string type or `onError` method will be called.
#### onMessage
this method needs to be implemented. it will be called once a message arrives from native side. The type of message is in string.
#### onError
this is an error reporting method. It will be called if there is an error happens during sending a message. It receives a error message in string type.
## Notes
> a special bridge script will be injected once the page is going to different URL. So you don't have to manage when it needs to be injected.
> You can still pass your own javascript to be injected into webview. However, Bridge script will be injected first and then your custom script.
## Simple Example
This example can be found in `examples` folder.
```js
const injectScript = `
(function () {
if (WebViewBridge) {
WebViewBridge.onMessage = function (message) {
alert('got a message from Native: ' + message);
WebViewBridge.send("message from webview");
};
}
}());
`;
var Sample2 = React.createClass({
componentDidMount() {
var webviewRef = this.refs[WEBVIEW_REF];
webviewRef.onMessage(function (message) {
console.log("This message coming from web view", message);
webviewRef.send("Hello from react-native");
});
webviewRef.injectBridgeScript();
}
render() {
setTimeout(() => {
this.refs.webviewbridge.sendToBridge("hahaha");
}, 5000);
},
onBridgeMessage: function (message) {
console.log(message);
},
render: function() {
return (
<WebViewBridge
ref={WEBVIEW_REF}
url="http://<my awesome project url>"
style={{flex: 1}}
/>
ref="webviewbridge"
onBridgeMessage={this.onBridgeMessage}
injectedJavaScript={injectScript}
onBridgeMessage={(message) => {
console.log(message);
}}
url={"http://google.com"}/>
);
}
}
Added feature
- 0.3.4
- added `print` feature [exampl ecode](https://github.com/alinz/react-native-webview-bridge/blob/v0.3.4/example/Sample1/index.ios.js#L53)
});
```
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

-29
View File
@@ -1,29 +0,0 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.sample1"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false // Set this to true to enable Proguard
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.facebook.react:react-native:0.13.+'
}
@@ -1,3 +0,0 @@
<resources>
<string name="app_name">Sample1</string>
</resources>
-3
View File
@@ -1,3 +0,0 @@
rootProject.name = 'Sample1'
include ':app'
-8
View File
@@ -1,8 +0,0 @@
// Offline JS
// To re-generate the offline bundle, run this from the root of your project:
//
// $ react-native bundle --minify
//
// See http://facebook.github.io/react-native/docs/runningondevice.html for more details.
throw new Error('Offline JS file is empty. See iOS/main.jsbundle for instructions');
-83
View File
@@ -1,83 +0,0 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
'use strict';
var React = require('react-native');
var WebViewBridgeNative = require('react-native-webview-bridge');
var myPageScripts = function() {
function WebViewBridgeReady(cb) {
if (window.WebViewBridge) {
cb(window.WebViewBridge);
return;
}
function handler() {
document.removeEventListener('WebViewBridge', handler, false);
cb(window.WebViewBridge);
}
document.addEventListener('WebViewBridge', handler, false);
}
WebViewBridgeReady(function (WebViewBridge) {
WebViewBridge.send("Hello this is me calling from web page");
});
};
//convert function definition into string
myPageScripts = `(${myPageScripts.toString()}());`;
var {
AppRegistry,
Component
} = React;
class Sample1 extends Component {
constructor(props) {
super(props);
this.once = true;
}
componentDidMount() {
var myWebViewBridgeRef = this.refs.myWebViewBridge;
var onMessage = function(message) {
console.log("Received message", message);
};
myWebViewBridgeRef.onMessage(onMessage);
myWebViewBridgeRef.injectBridgeScript();
//this opens up the printer window dialoge
setTimeout(() => {
myWebViewBridgeRef.print();
}, 5000);
}
onNavigationStateChange(navState) {
var myWebViewBridgeRef = this.refs.myWebViewBridge;
if (this.once) {
this.once = false;
setTimeout(() => {
myWebViewBridgeRef.evalScript(myPageScripts);
}, 1000);
}
console.log(navState.url);
}
render() {
var url = 'http://google.com';
return (
<WebViewBridgeNative
ref="myWebViewBridge"
onNavigationStateChange={this.onNavigationStateChange.bind(this)}
url={url}
style={{flex: 1}}/>
);
}
}
AppRegistry.registerComponent('Sample1', () => Sample1);
@@ -7,12 +7,24 @@
# Some modules have their own node_modules with overlap
.*/node_modules/node-haste/.*
# Ignore react-tools where there are overlaps, but don't ignore anything that
# react-native relies on
.*/node_modules/react-tools/src/React.js
.*/node_modules/react-tools/src/renderers/shared/event/EventPropagators.js
.*/node_modules/react-tools/src/renderers/shared/event/eventPlugins/ResponderEventPlugin.js
.*/node_modules/react-tools/src/shared/vendor/core/ExecutionEnvironment.js
# Ugh
.*/node_modules/babel.*
.*/node_modules/babylon.*
.*/node_modules/invariant.*
# Ignore react and fbjs where there are overlaps, but don't ignore
# anything that react-native relies on
.*/node_modules/fbjs-haste/.*/__tests__/.*
.*/node_modules/fbjs-haste/__forks__/Map.js
.*/node_modules/fbjs-haste/__forks__/Promise.js
.*/node_modules/fbjs-haste/__forks__/fetch.js
.*/node_modules/fbjs-haste/core/ExecutionEnvironment.js
.*/node_modules/fbjs-haste/core/isEmpty.js
.*/node_modules/fbjs-haste/crypto/crc32.js
.*/node_modules/fbjs-haste/stubs/ErrorUtils.js
.*/node_modules/react-haste/React.js
.*/node_modules/react-haste/renderers/dom/ReactDOM.js
.*/node_modules/react-haste/renderers/shared/event/eventPlugins/ResponderEventPlugin.js
# Ignore commoner tests
.*/node_modules/commoner/test/.*
@@ -43,9 +55,9 @@ suppress_type=$FlowIssue
suppress_type=$FlowFixMe
suppress_type=$FixMe
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-7]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-7]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(1[0-8]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(1[0-8]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)? #[0-9]+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
[version]
0.17.0
0.18.1
+78
View File
@@ -0,0 +1,78 @@
apply plugin: "com.android.application"
/**
* The react.gradle file registers two tasks: bundleDebugJsAndAssets and bundleReleaseJsAndAssets.
* These basically call `react-native bundle` with the correct arguments during the Android build
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
* bundle directly from the development server. Below you can see all the possible configurations
* and their defaults. If you decide to add a configuration block, make sure to add it before the
* `apply from: "react.gradle"` line.
*
* project.ext.react = [
* // the name of the generated asset file containing your JS bundle
* bundleAssetName: "index.android.bundle",
*
* // the entry file for bundle generation
* entryFile: "index.android.js",
*
* // whether to bundle JS and assets in debug mode
* bundleInDebug: false,
*
* // whether to bundle JS and assets in release mode
* bundleInRelease: true,
*
* // the root of your project, i.e. where "package.json" lives
* root: "../../",
*
* // where to put the JS bundle asset in debug mode
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
*
* // where to put the JS bundle asset in release mode
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in debug mode
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
*
* // where to put drawable resources / React Native assets, e.g. the ones you use via
* // require('./image.png')), in release mode
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
*
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
* // for example, you might want to remove it from here.
* inputExcludes: ["android/**", "ios/**"]
* ]
*/
apply from: "react.gradle"
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
applicationId "com.sample2"
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
ndk {
abiFilters "armeabi-v7a", "x86"
}
}
buildTypes {
release {
minifyEnabled false // Set this to true to enable Proguard
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
}
}
}
dependencies {
compile fileTree(dir: "libs", include: ["*.jar"])
compile "com.android.support:appcompat-v7:23.0.1"
compile "com.facebook.react:react-native:0.16.+"
}
+87
View File
@@ -0,0 +1,87 @@
import org.apache.tools.ant.taskdefs.condition.Os
def config = project.hasProperty("react") ? project.react : [];
def bundleAssetName = config.bundleAssetName ?: "index.android.bundle"
def entryFile = config.entryFile ?: "index.android.js"
// because elvis operator
def elvisFile(thing) {
return thing ? file(thing) : null;
}
def reactRoot = elvisFile(config.root) ?: file("../../")
def jsBundleDirDebug = elvisFile(config.jsBundleDirDebug) ?:
file("$buildDir/intermediates/assets/debug")
def jsBundleDirRelease = elvisFile(config.jsBundleDirRelease) ?:
file("$buildDir/intermediates/assets/release")
def resourcesDirDebug = elvisFile(config.resourcesDirDebug) ?:
file("$buildDir/intermediates/res/merged/debug")
def resourcesDirRelease = elvisFile(config.resourcesDirRelease) ?:
file("$buildDir/intermediates/res/merged/release")
def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"]
def jsBundleFileDebug = file("$jsBundleDirDebug/$bundleAssetName")
def jsBundleFileRelease = file("$jsBundleDirRelease/$bundleAssetName")
task bundleDebugJsAndAssets(type: Exec) {
// create dirs if they are not there (e.g. the "clean" task just ran)
doFirst {
jsBundleDirDebug.mkdirs()
resourcesDirDebug.mkdirs()
}
// set up inputs and outputs so gradle can cache the result
inputs.files fileTree(dir: reactRoot, excludes: inputExcludes)
outputs.dir jsBundleDirDebug
outputs.dir resourcesDirDebug
// set up the call to the react-native cli
workingDir reactRoot
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "true", "--entry-file",
entryFile, "--bundle-output", jsBundleFileDebug, "--assets-dest", resourcesDirDebug
} else {
commandLine "react-native", "bundle", "--platform", "android", "--dev", "true", "--entry-file",
entryFile, "--bundle-output", jsBundleFileDebug, "--assets-dest", resourcesDirDebug
}
enabled config.bundleInDebug ?: false
}
task bundleReleaseJsAndAssets(type: Exec) {
// create dirs if they are not there (e.g. the "clean" task just ran)
doFirst {
jsBundleDirRelease.mkdirs()
resourcesDirRelease.mkdirs()
}
// set up inputs and outputs so gradle can cache the result
inputs.files fileTree(dir: reactRoot, excludes: inputExcludes)
outputs.dir jsBundleDirRelease
outputs.dir resourcesDirRelease
// set up the call to the react-native cli
workingDir reactRoot
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
commandLine "cmd","/c", "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file",
entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease
} else {
commandLine "react-native", "bundle", "--platform", "android", "--dev", "false", "--entry-file",
entryFile, "--bundle-output", jsBundleFileRelease, "--assets-dest", resourcesDirRelease
}
enabled config.bundleInRelease ?: true
}
gradle.projectsEvaluated {
// hook bundleDebugJsAndAssets into the android build process
bundleDebugJsAndAssets.dependsOn mergeDebugResources
bundleDebugJsAndAssets.dependsOn mergeDebugAssets
processDebugResources.dependsOn bundleDebugJsAndAssets
// hook bundleReleaseJsAndAssets into the android build process
bundleReleaseJsAndAssets.dependsOn mergeReleaseResources
bundleReleaseJsAndAssets.dependsOn mergeReleaseAssets
processReleaseResources.dependsOn bundleReleaseJsAndAssets
}
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sample1">
package="com.sample2">
<uses-permission android:name="android.permission.INTERNET" />
@@ -10,7 +10,8 @@
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -1,4 +1,4 @@
package com.sample1;
package com.sample2;
import android.app.Activity;
import android.os.Bundle;
@@ -30,7 +30,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "Sample1", null);
mReactRootView.startReactApplication(mReactInstanceManager, "Sample2", null);
setContentView(mReactRootView);
}
@@ -72,7 +72,7 @@ public class MainActivity extends Activity implements DefaultHardwareBackBtnHand
super.onResume();
if (mReactInstanceManager != null) {
mReactInstanceManager.onResume(this);
mReactInstanceManager.onResume(this, this);
}
}
}

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

@@ -0,0 +1,3 @@
<resources>
<string name="app_name">Sample2</string>
</resources>
@@ -16,5 +16,8 @@ allprojects {
repositories {
mavenLocal()
jcenter()
jcenter {
url "http://dl.bintray.com/mkonicek/maven"
}
}
}
+3
View File
@@ -0,0 +1,3 @@
rootProject.name = 'Sample2'
include ':app'
@@ -12,7 +12,7 @@ var {
View,
} = React;
var Sample1 = React.createClass({
var Sample2 = React.createClass({
render: function() {
return (
<View style={styles.container}>
@@ -49,4 +49,4 @@ var styles = StyleSheet.create({
},
});
AppRegistry.registerComponent('Sample1', () => Sample1);
AppRegistry.registerComponent('Sample2', () => Sample2);
+54
View File
@@ -0,0 +1,54 @@
/**
* Sample React Native App
* https://github.com/facebook/react-native
*/
'use strict';
var React = require('react-native');
var {
AppRegistry,
StyleSheet,
Text,
View,
} = React;
var WebViewBridge = require('react-native-webview-bridge');
const injectScript = `
(function () {
if (WebViewBridge) {
WebViewBridge.onMessage = function (message) {
alert('got a message from Native: ' + message);
WebViewBridge.send("message from webview");
};
}
}());
`;
var Sample2 = React.createClass({
componentDidMount() {
setTimeout(() => {
this.refs.webviewbridge.sendToBridge("hahaha");
}, 5000);
},
onBridgeMessage: function (message) {
console.log(message);
},
render: function() {
return (
<WebViewBridge
ref="webviewbridge"
onBridgeMessage={this.onBridgeMessage}
injectedJavaScript={injectScript}
onBridgeMessage={(message) => {
console.log(message);
}}
url={"http://google.com"}/>
);
}
});
AppRegistry.registerComponent('Sample2', () => Sample2);
@@ -7,13 +7,12 @@
objects = {
/* Begin PBXBuildFile section */
008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */ = {isa = PBXBuildFile; fileRef = 008F07F21AC5B25A0029DE68 /* main.jsbundle */; };
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; };
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
00E356F31AD99517003FC87E /* Sample1Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* Sample1Tests.m */; };
00E356F31AD99517003FC87E /* Sample2Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* Sample2Tests.m */; };
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
@@ -22,7 +21,7 @@
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; };
415EA5581BEB154F000C8125 /* libReact-Native-WebView-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 415EA5561BEB1546000C8125 /* libReact-Native-WebView-Bridge.a */; };
4115A2051C189D3C0020D542 /* libReact-Native-Webview-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4115A2041C189C290020D542 /* libReact-Native-Webview-Bridge.a */; };
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
/* End PBXBuildFile section */
@@ -67,7 +66,7 @@
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
remoteInfo = Sample1;
remoteInfo = Sample2;
};
139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
@@ -90,12 +89,12 @@
remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192;
remoteInfo = React;
};
415EA5551BEB1546000C8125 /* PBXContainerItemProxy */ = {
4115A2031C189C290020D542 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 415EA5481BEB1546000C8125 /* React-Native-WebView-Bridge.xcodeproj */;
containerPortal = 4115A1FF1C189C290020D542 /* React-Native-Webview-Bridge.xcodeproj */;
proxyType = 2;
remoteGlobalIDString = 415125421BEB04C40042928F;
remoteInfo = "React-Native-WebView-Bridge";
remoteGlobalIDString = 4114DC4C1C187C3A003CD988;
remoteInfo = "React-Native-Webview-Bridge";
};
78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
@@ -120,20 +119,20 @@
00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = "<group>"; };
00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = "<group>"; };
00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = "<group>"; };
00E356EE1AD99517003FC87E /* Sample1Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Sample1Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356EE1AD99517003FC87E /* Sample2Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Sample2Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* Sample1Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Sample1Tests.m; sourceTree = "<group>"; };
00E356F21AD99517003FC87E /* Sample2Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Sample2Tests.m; sourceTree = "<group>"; };
139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = "<group>"; };
139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Sample1.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample1.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Sample1/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Sample1/AppDelegate.m; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* Sample2.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Sample2.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Sample2/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = Sample2/AppDelegate.m; sourceTree = "<group>"; };
13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Sample1/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sample1/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Sample1/main.m; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Sample2/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Sample2/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Sample2/main.m; sourceTree = "<group>"; };
146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = "<group>"; };
415EA5481BEB1546000C8125 /* React-Native-WebView-Bridge.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "React-Native-WebView-Bridge.xcodeproj"; path = "../node_modules/react-native-webview-bridge/lib/ios/React-Native-WebView-Bridge.xcodeproj"; sourceTree = "<group>"; };
4115A1FF1C189C290020D542 /* React-Native-Webview-Bridge.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "React-Native-Webview-Bridge.xcodeproj"; path = "../node_modules/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj"; sourceTree = "<group>"; };
78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = "<group>"; };
832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -150,7 +149,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
415EA5581BEB154F000C8125 /* libReact-Native-WebView-Bridge.a in Frameworks */,
4115A2051C189D3C0020D542 /* libReact-Native-Webview-Bridge.a in Frameworks */,
146834051AC3E58100842450 /* libReact.a in Frameworks */,
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */,
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */,
@@ -207,13 +206,13 @@
name = Products;
sourceTree = "<group>";
};
00E356EF1AD99517003FC87E /* Sample1Tests */ = {
00E356EF1AD99517003FC87E /* Sample2Tests */ = {
isa = PBXGroup;
children = (
00E356F21AD99517003FC87E /* Sample1Tests.m */,
00E356F21AD99517003FC87E /* Sample2Tests.m */,
00E356F01AD99517003FC87E /* Supporting Files */,
);
path = Sample1Tests;
path = Sample2Tests;
sourceTree = "<group>";
};
00E356F01AD99517003FC87E /* Supporting Files */ = {
@@ -240,7 +239,7 @@
name = Products;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* Sample1 */ = {
13B07FAE1A68108700A75B9A /* Sample2 */ = {
isa = PBXGroup;
children = (
008F07F21AC5B25A0029DE68 /* main.jsbundle */,
@@ -251,7 +250,7 @@
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
);
name = Sample1;
name = Sample2;
sourceTree = "<group>";
};
146834001AC3E56700842450 /* Products */ = {
@@ -262,10 +261,10 @@
name = Products;
sourceTree = "<group>";
};
415EA5491BEB1546000C8125 /* Products */ = {
4115A2001C189C290020D542 /* Products */ = {
isa = PBXGroup;
children = (
415EA5561BEB1546000C8125 /* libReact-Native-WebView-Bridge.a */,
4115A2041C189C290020D542 /* libReact-Native-Webview-Bridge.a */,
);
name = Products;
sourceTree = "<group>";
@@ -281,7 +280,7 @@
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
isa = PBXGroup;
children = (
415EA5481BEB1546000C8125 /* React-Native-WebView-Bridge.xcodeproj */,
4115A1FF1C189C290020D542 /* React-Native-Webview-Bridge.xcodeproj */,
146833FF1AC3E56700842450 /* React.xcodeproj */,
00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */,
00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */,
@@ -307,9 +306,9 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* Sample1 */,
13B07FAE1A68108700A75B9A /* Sample2 */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
00E356EF1AD99517003FC87E /* Sample1Tests */,
00E356EF1AD99517003FC87E /* Sample2Tests */,
83CBBA001A601CBA00E9B192 /* Products */,
);
indentWidth = 2;
@@ -319,8 +318,8 @@
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* Sample1.app */,
00E356EE1AD99517003FC87E /* Sample1Tests.xctest */,
13B07F961A680F5B00A75B9A /* Sample2.app */,
00E356EE1AD99517003FC87E /* Sample2Tests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -328,9 +327,9 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
00E356ED1AD99517003FC87E /* Sample1Tests */ = {
00E356ED1AD99517003FC87E /* Sample2Tests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "Sample1Tests" */;
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "Sample2Tests" */;
buildPhases = (
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
@@ -341,26 +340,27 @@
dependencies = (
00E356F51AD99517003FC87E /* PBXTargetDependency */,
);
name = Sample1Tests;
productName = Sample1Tests;
productReference = 00E356EE1AD99517003FC87E /* Sample1Tests.xctest */;
name = Sample2Tests;
productName = Sample2Tests;
productReference = 00E356EE1AD99517003FC87E /* Sample2Tests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
13B07F861A680F5B00A75B9A /* Sample1 */ = {
13B07F861A680F5B00A75B9A /* Sample2 */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sample1" */;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sample2" */;
buildPhases = (
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
);
buildRules = (
);
dependencies = (
);
name = Sample1;
name = Sample2;
productName = "Hello World";
productReference = 13B07F961A680F5B00A75B9A /* Sample1.app */;
productReference = 13B07F961A680F5B00A75B9A /* Sample2.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -378,7 +378,7 @@
};
};
};
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Sample1" */;
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Sample2" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
@@ -427,8 +427,8 @@
ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */;
},
{
ProductGroup = 415EA5491BEB1546000C8125 /* Products */;
ProjectRef = 415EA5481BEB1546000C8125 /* React-Native-WebView-Bridge.xcodeproj */;
ProductGroup = 4115A2001C189C290020D542 /* Products */;
ProjectRef = 4115A1FF1C189C290020D542 /* React-Native-Webview-Bridge.xcodeproj */;
},
{
ProductGroup = 146834001AC3E56700842450 /* Products */;
@@ -437,8 +437,8 @@
);
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* Sample1 */,
00E356ED1AD99517003FC87E /* Sample1Tests */,
13B07F861A680F5B00A75B9A /* Sample2 */,
00E356ED1AD99517003FC87E /* Sample2Tests */,
);
};
/* End PBXProject section */
@@ -500,11 +500,11 @@
remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
415EA5561BEB1546000C8125 /* libReact-Native-WebView-Bridge.a */ = {
4115A2041C189C290020D542 /* libReact-Native-Webview-Bridge.a */ = {
isa = PBXReferenceProxy;
fileType = archive.ar;
path = "libReact-Native-WebView-Bridge.a";
remoteRef = 415EA5551BEB1546000C8125 /* PBXContainerItemProxy */;
path = "libReact-Native-Webview-Bridge.a";
remoteRef = 4115A2031C189C290020D542 /* PBXContainerItemProxy */;
sourceTree = BUILT_PRODUCTS_DIR;
};
78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = {
@@ -535,7 +535,6 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
008F07F31AC5B25A0029DE68 /* main.jsbundle in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
);
@@ -543,12 +542,29 @@
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Bundle React Native code and images";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "../node_modules/react-native/packager/react-native-xcode.sh";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
00E356EA1AD99517003FC87E /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
00E356F31AD99517003FC87E /* Sample1Tests.m in Sources */,
00E356F31AD99517003FC87E /* Sample2Tests.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -566,7 +582,7 @@
/* Begin PBXTargetDependency section */
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 13B07F861A680F5B00A75B9A /* Sample1 */;
target = 13B07F861A680F5B00A75B9A /* Sample2 */;
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
@@ -578,7 +594,7 @@
13B07FB21A68108700A75B9A /* Base */,
);
name = LaunchScreen.xib;
path = Sample1;
path = Sample2;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
@@ -596,11 +612,11 @@
"DEBUG=1",
"$(inherited)",
);
INFOPLIST_FILE = Sample1Tests/Info.plist;
INFOPLIST_FILE = Sample2Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample1.app/Sample1";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample2.app/Sample2";
};
name = Debug;
};
@@ -613,11 +629,11 @@
"$(SDKROOT)/Developer/Library/Frameworks",
"$(inherited)",
);
INFOPLIST_FILE = Sample1Tests/Info.plist;
INFOPLIST_FILE = Sample2Tests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.2;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample1.app/Sample1";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Sample2.app/Sample2";
};
name = Release;
};
@@ -631,10 +647,10 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
);
INFOPLIST_FILE = Sample1/Info.plist;
INFOPLIST_FILE = Sample2/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = Sample1;
PRODUCT_NAME = Sample2;
};
name = Debug;
};
@@ -647,10 +663,10 @@
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
"$(SRCROOT)/../node_modules/react-native/React/**",
);
INFOPLIST_FILE = Sample1/Info.plist;
INFOPLIST_FILE = Sample2/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = Sample1;
PRODUCT_NAME = Sample2;
};
name = Release;
};
@@ -743,7 +759,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "Sample1Tests" */ = {
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "Sample2Tests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
00E356F61AD99517003FC87E /* Debug */,
@@ -752,7 +768,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sample1" */ = {
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Sample2" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
@@ -761,7 +777,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Sample1" */ = {
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Sample2" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,
@@ -15,9 +15,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Sample1.app"
BlueprintName = "Sample1"
ReferencedContainer = "container:Sample1.xcodeproj">
BuildableName = "Sample2.app"
BlueprintName = "Sample2"
ReferencedContainer = "container:Sample2.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
@@ -29,9 +29,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "Sample1Tests.xctest"
BlueprintName = "Sample1Tests"
ReferencedContainer = "container:Sample1.xcodeproj">
BuildableName = "Sample2Tests.xctest"
BlueprintName = "Sample2Tests"
ReferencedContainer = "container:Sample2.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -47,9 +47,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "Sample1Tests.xctest"
BlueprintName = "Sample1Tests"
ReferencedContainer = "container:Sample1.xcodeproj">
BuildableName = "Sample2Tests.xctest"
BlueprintName = "Sample2Tests"
ReferencedContainer = "container:Sample2.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
@@ -57,9 +57,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Sample1.app"
BlueprintName = "Sample1"
ReferencedContainer = "container:Sample1.xcodeproj">
BuildableName = "Sample2.app"
BlueprintName = "Sample2"
ReferencedContainer = "container:Sample2.xcodeproj">
</BuildableReference>
</MacroExpansion>
</TestAction>
@@ -77,9 +77,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Sample1.app"
BlueprintName = "Sample1"
ReferencedContainer = "container:Sample1.xcodeproj">
BuildableName = "Sample2.app"
BlueprintName = "Sample2"
ReferencedContainer = "container:Sample2.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
@@ -96,9 +96,9 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "Sample1.app"
BlueprintName = "Sample1"
ReferencedContainer = "container:Sample1.xcodeproj">
BuildableName = "Sample2.app"
BlueprintName = "Sample2"
ReferencedContainer = "container:Sample2.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
@@ -35,23 +35,19 @@
/**
* OPTION 2
* Load from pre-bundled file on disk. To re-generate the static bundle
* from the root of your project directory, run
*
* $ react-native bundle --minify
*
* see http://facebook.github.io/react-native/docs/runningondevice.html
* Load from pre-bundled file on disk. The static bundle is automatically
* generated by "Bundle React Native code and images" build step.
*/
// jsCodeLocation = [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
moduleName:@"Sample1"
moduleName:@"Sample2"
initialProperties:nil
launchOptions:launchOptions];
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
UIViewController *rootViewController = [[UIViewController alloc] init];
UIViewController *rootViewController = [UIViewController new];
rootViewController.view = rootView;
self.window.rootViewController = rootViewController;
[self.window makeKeyAndVisible];
@@ -18,7 +18,7 @@
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sample1" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sample2" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
<rect key="frame" x="20" y="140" width="441" height="43"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
@@ -16,11 +16,11 @@
#define TIMEOUT_SECONDS 240
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
@interface Sample1Tests : XCTestCase
@interface Sample2Tests : XCTestCase
@end
@implementation Sample1Tests
@implementation Sample2Tests
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
{
@@ -42,7 +42,7 @@
BOOL foundElement = NO;
__block NSString *redboxError = nil;
RCTSetLogFunction(^(RCTLogLevel level, NSString *fileName, NSNumber *lineNumber, NSString *message) {
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
if (level >= RCTLogLevelError) {
redboxError = message;
}
@@ -1,12 +1,12 @@
{
"name": "Sample1",
"name": "Sample2",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node_modules/react-native/packager/packager.sh"
"start": "react-native start"
},
"dependencies": {
"react-native": "^0.13.2",
"react-native": "^0.16.0",
"react-native-webview-bridge": "../.."
}
}
+47
View File
@@ -0,0 +1,47 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz)
* All rights reserved
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTView.h"
@class RCTWebViewBridge;
/**
* Special scheme used to pass messages to the injectedJavaScript
* code without triggering a page load. Usage:
*
* window.location.href = RCTJSNavigationScheme + '://hello'
*/
extern NSString *const RCTJSNavigationScheme;
@protocol RCTWebViewBridgeDelegate <NSObject>
- (BOOL)webView:(RCTWebViewBridge *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback;
@end
@interface RCTWebViewBridge : RCTView
@property (nonatomic, weak) id<RCTWebViewBridgeDelegate> delegate;
@property (nonatomic, strong) NSURL *URL;
@property (nonatomic, assign) UIEdgeInsets contentInset;
@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets;
@property (nonatomic, copy) NSString *injectedJavaScript;
- (void)goForward;
- (void)goBack;
- (void)reload;
- (void)sendToBridge:(NSString *)message;
@end
+347
View File
@@ -0,0 +1,347 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz)
* All rights reserved
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTWebViewBridge.h"
#import <UIKit/UIKit.h>
#import "RCTAutoInsetsProtocol.h"
#import "RCTEventDispatcher.h"
#import "RCTLog.h"
#import "RCTUtils.h"
#import "RCTView.h"
#import "UIView+React.h"
//This is a very elegent way of defining multiline string in objective-c.
//source: http://stackoverflow.com/a/23387659/828487
#define NSStringMultiline(...) [[NSString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding]
//we don'e need this one since it has been defined in RCTWebView.m
//NSString *const RCTJSNavigationScheme = @"react-js-navigation";
NSString *const RCTWebViewBridgeSchema = @"wvb";
@interface RCTWebViewBridge () <UIWebViewDelegate, RCTAutoInsetsProtocol>
@property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
@property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
@property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest;
@property (nonatomic, copy) RCTDirectEventBlock onBridgeMessage;
@end
@implementation RCTWebViewBridge
{
UIWebView *_webView;
NSString *_injectedJavaScript;
}
- (instancetype)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame])) {
super.backgroundColor = [UIColor clearColor];
_automaticallyAdjustContentInsets = YES;
_contentInset = UIEdgeInsetsZero;
_webView = [[UIWebView alloc] initWithFrame:self.bounds];
_webView.delegate = self;
[self addSubview:_webView];
}
return self;
}
RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder)
- (void)goForward
{
[_webView goForward];
}
- (void)goBack
{
[_webView goBack];
}
- (void)reload
{
[_webView reload];
}
- (void)sendToBridge:(NSString *)message
{
//we are warpping the send message in a function to make sure that if
//WebView is not injected, we don't crash the app.
NSString *format = NSStringMultiline(
(function(){
if (WebViewBridge && WebViewBridge.__push__) {
WebViewBridge.__push__('%@');
}
}());
);
NSString *command = [NSString stringWithFormat: format, message];
[_webView stringByEvaluatingJavaScriptFromString:command];
}
- (NSURL *)URL
{
return _webView.request.URL;
}
- (void)setURL:(NSURL *)URL
{
// Because of the way React works, as pages redirect, we actually end up
// passing the redirect urls back here, so we ignore them if trying to load
// the same url. We'll expose a call to 'reload' to allow a user to load
// the existing page.
if ([URL isEqual:_webView.request.URL]) {
return;
}
if (!URL) {
// Clear the webview
[_webView loadHTMLString:@"" baseURL:nil];
return;
}
[_webView loadRequest:[NSURLRequest requestWithURL:URL]];
}
- (void)setHTML:(NSString *)HTML
{
[_webView loadHTMLString:HTML baseURL:nil];
}
- (void)layoutSubviews
{
[super layoutSubviews];
_webView.frame = self.bounds;
}
- (void)setContentInset:(UIEdgeInsets)contentInset
{
_contentInset = contentInset;
[RCTView autoAdjustInsetsForView:self
withScrollView:_webView.scrollView
updateOffset:NO];
}
- (void)setBackgroundColor:(UIColor *)backgroundColor
{
CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
self.opaque = _webView.opaque = (alpha == 1.0);
_webView.backgroundColor = backgroundColor;
}
- (UIColor *)backgroundColor
{
return _webView.backgroundColor;
}
- (NSMutableDictionary<NSString *, id> *)baseEvent
{
NSMutableDictionary<NSString *, id> *event = [[NSMutableDictionary alloc] initWithDictionary:@{
@"url": _webView.request.URL.absoluteString ?: @"",
@"loading" : @(_webView.loading),
@"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"],
@"canGoBack": @(_webView.canGoBack),
@"canGoForward" : @(_webView.canGoForward),
}];
return event;
}
- (void)refreshContentInset
{
[RCTView autoAdjustInsetsForView:self
withScrollView:_webView.scrollView
updateOffset:YES];
}
#pragma mark - UIWebViewDelegate methods
- (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request
navigationType:(UIWebViewNavigationType)navigationType
{
BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme];
if (!isJSNavigation && [request.URL.scheme isEqualToString:RCTWebViewBridgeSchema]) {
NSString* message = [webView stringByEvaluatingJavaScriptFromString:@"WebViewBridge.__fetch__()"];
NSMutableDictionary<NSString *, id> *onBridgeMessageEvent = [[NSMutableDictionary alloc] initWithDictionary:@{
@"messages": [self stringArrayJsonToArray: message]
}];
_onBridgeMessage(onBridgeMessageEvent);
isJSNavigation = YES;
}
// skip this for the JS Navigation handler
if (!isJSNavigation && _onShouldStartLoadWithRequest) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": (request.URL).absoluteString,
@"navigationType": @(navigationType)
}];
if (![self.delegate webView:self
shouldStartLoadForRequest:event
withCallback:_onShouldStartLoadWithRequest]) {
return NO;
}
}
if (_onLoadingStart) {
// We have this check to filter out iframe requests and whatnot
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
if (isTopFrame) {
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"url": (request.URL).absoluteString,
@"navigationType": @(navigationType)
}];
_onLoadingStart(event);
}
}
// JS Navigation handler
return !isJSNavigation;
}
- (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error
{
if (_onLoadingError) {
if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) {
// NSURLErrorCancelled is reported when a page has a redirect OR if you load
// a new URL in the WebView before the previous one came back. We can just
// ignore these since they aren't real errors.
// http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os
return;
}
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
[event addEntriesFromDictionary:@{
@"domain": error.domain,
@"code": @(error.code),
@"description": error.localizedDescription,
}];
_onLoadingError(event);
}
}
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
//injecting WebViewBridge Script
NSString *webViewBridgeScriptContent = [self webViewBridgeScript];
[webView stringByEvaluatingJavaScriptFromString:webViewBridgeScriptContent];
//////////////////////////////////////////////////////////////////////////////
if (_injectedJavaScript != nil) {
NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript];
NSMutableDictionary<NSString *, id> *event = [self baseEvent];
event[@"jsEvaluationValue"] = jsEvaluationValue;
_onLoadingFinish(event);
}
// we only need the final 'finishLoad' call so only fire the event when we're actually done loading.
else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) {
_onLoadingFinish([self baseEvent]);
}
}
- (NSArray*)stringArrayJsonToArray:(NSString *)message
{
return [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:nil];
}
//since there is no easy way to load the static lib resource in ios,
//we are loading the script from this method.
- (NSString *)webViewBridgeScript {
// NSBundle *bundle = [NSBundle mainBundle];
// NSString *webViewBridgeScriptFile = [bundle pathForResource:@"webviewbridge"
// ofType:@"js"];
// NSString *webViewBridgeScriptContent = [NSString stringWithContentsOfFile:webViewBridgeScriptFile
// encoding:NSUTF8StringEncoding
// error:nil];
return NSStringMultiline(
(function (window) {
'use strict';
//Make sure that if WebViewBridge already in scope we don't override it.
if (window.WebViewBridge) {
return;
}
var RNWBSchema = 'wvb';
var sendQueue = [];
var receiveQueue = [];
function callFunc(func, message) {
if ('function' === typeof func) {
func(message);
}
}
function signalNative() {
window.location = RNWBSchema + '://message' + new Date().getTime();
}
//I made the private function ugly signiture so user doesn't called them accidently.
//if you do, then I have nothing to say. :(
var WebViewBridge = {
//this function will be called by native side to push a new message
//to webview.
__push__: function (message) {
receiveQueue.push(message);
//reason I need this setTmeout is to return this function as fast as
//possible to release the native side thread.
setTimeout(function () {
var message = receiveQueue.pop();
callFunc(WebViewBridge.onMessage, message);
}, 15); //this magic number is just a random small value. I don't like 0.
},
__fetch__: function () {
//since our sendQueue array only contains string, and our connection to native
//can only accept string, we need to convert array of strings into single string.
var messages = JSON.stringify(sendQueue);
//we make sure that sendQueue is resets
sendQueue = [];
//return the messages back to native side.
return messages;
},
//make sure message is string. because only string can be sent to native,
//if you don't pass it as string, onError function will be called.
send: function (message) {
if ('string' !== typeof message) {
callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string");
return;
}
//we queue the messages to make sure that native can collects all of them in one shot.
sendQueue.push(message);
//signal the objective-c that there is a message in the queue
signalNative();
},
onMessage: null,
onError: null
};
window.WebViewBridge = WebViewBridge;
}(window));
);
}
@end
+17
View File
@@ -0,0 +1,17 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz)
* All rights reserved
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTViewManager.h"
@interface RCTWebViewBridgeManager : RCTViewManager
@end
+152
View File
@@ -0,0 +1,152 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz)
* All rights reserved
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
#import "RCTWebViewBridgeManager.h"
#import "RCTBridge.h"
#import "RCTUIManager.h"
#import "RCTWebViewBridge.h"
#import "UIView+React.h"
@interface RCTWebViewBridgeManager () <RCTWebViewBridgeDelegate>
@end
@implementation RCTWebViewBridgeManager
{
NSConditionLock *_shouldStartLoadLock;
BOOL _shouldStartLoad;
}
RCT_EXPORT_MODULE()
- (UIView *)view
{
RCTWebViewBridge *webView = [RCTWebViewBridge new];
webView.delegate = self;
return webView;
}
RCT_REMAP_VIEW_PROPERTY(url, URL, NSURL)
RCT_REMAP_VIEW_PROPERTY(html, HTML, NSString)
RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL)
RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL)
RCT_REMAP_VIEW_PROPERTY(scalesPageToFit, _webView.scalesPageToFit, BOOL)
RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
RCT_REMAP_VIEW_PROPERTY(allowsInlineMediaPlayback, _webView.allowsInlineMediaPlayback, BOOL)
RCT_EXPORT_VIEW_PROPERTY(onBridgeMessage, RCTDirectEventBlock)
- (NSDictionary<NSString *, id> *)constantsToExport
{
return @{
@"JSNavigationScheme": RCTJSNavigationScheme,
@"NavigationType": @{
@"LinkClicked": @(UIWebViewNavigationTypeLinkClicked),
@"FormSubmitted": @(UIWebViewNavigationTypeFormSubmitted),
@"BackForward": @(UIWebViewNavigationTypeBackForward),
@"Reload": @(UIWebViewNavigationTypeReload),
@"FormResubmitted": @(UIWebViewNavigationTypeFormResubmitted),
@"Other": @(UIWebViewNavigationTypeOther)
},
};
}
RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebViewBridge *> *viewRegistry) {
RCTWebViewBridge *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebViewBridge class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view);
} else {
[view goBack];
}
}];
}
RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, UIView *> *viewRegistry) {
id view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebViewBridge class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view);
} else {
[view goForward];
}
}];
}
RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebViewBridge *> *viewRegistry) {
RCTWebViewBridge *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebViewBridge class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view);
} else {
[view reload];
}
}];
}
RCT_EXPORT_METHOD(sendToBridge:(nonnull NSNumber *)reactTag
value:(NSString*)message)
{
[self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary<NSNumber *, RCTWebViewBridge *> *viewRegistry) {
RCTWebViewBridge *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebViewBridge class]]) {
RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view);
} else {
[view sendToBridge: message];
}
}];
}
#pragma mark - Exported synchronous methods
- (BOOL)webView:(__unused RCTWebViewBridge *)webView
shouldStartLoadForRequest:(NSMutableDictionary<NSString *, id> *)request
withCallback:(RCTDirectEventBlock)callback
{
_shouldStartLoadLock = [[NSConditionLock alloc] initWithCondition:arc4random()];
_shouldStartLoad = YES;
request[@"lockIdentifier"] = @(_shouldStartLoadLock.condition);
callback(request);
// Block the main thread for a maximum of 250ms until the JS thread returns
if ([_shouldStartLoadLock lockWhenCondition:0 beforeDate:[NSDate dateWithTimeIntervalSinceNow:.25]]) {
BOOL returnValue = _shouldStartLoad;
[_shouldStartLoadLock unlock];
_shouldStartLoadLock = nil;
return returnValue;
} else {
RCTLogWarn(@"Did not receive response to shouldStartLoad in time, defaulting to YES");
return YES;
}
}
RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier)
{
if ([_shouldStartLoadLock tryLockWhenCondition:lockIdentifier]) {
_shouldStartLoad = result;
[_shouldStartLoadLock unlockWithCondition:0];
} else {
RCTLogWarn(@"startLoadWithResult invoked with invalid lockIdentifier: "
"got %zd, expected %zd", lockIdentifier, _shouldStartLoadLock.condition);
}
}
@end
@@ -7,39 +7,33 @@
objects = {
/* Begin PBXBuildFile section */
413672491BEB069E00E9FCEB /* RCTWebView+WebViewBridge.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4151254E1BEB05360042928F /* RCTWebView+WebViewBridge.h */; };
4136724A1BEB069E00E9FCEB /* RCTWebViewManager+WebViewManager.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 415125501BEB05360042928F /* RCTWebViewManager+WebViewManager.h */; };
4136724B1BEB069E00E9FCEB /* webview-bridge-script.js in CopyFiles */ = {isa = PBXBuildFile; fileRef = 415125521BEB05360042928F /* webview-bridge-script.js */; };
415125531BEB05360042928F /* RCTWebView+WebViewBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 4151254F1BEB05360042928F /* RCTWebView+WebViewBridge.m */; };
415125541BEB05360042928F /* RCTWebViewManager+WebViewManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 415125511BEB05360042928F /* RCTWebViewManager+WebViewManager.m */; };
4114DC5C1C187CCB003CD988 /* RCTWebViewBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */; };
4114DC5D1C187CCB003CD988 /* RCTWebViewBridgeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
415125401BEB04C40042928F /* CopyFiles */ = {
4114DC4A1C187C3A003CD988 /* CopyFiles */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "include/$(PRODUCT_NAME)";
dstSubfolderSpec = 16;
files = (
413672491BEB069E00E9FCEB /* RCTWebView+WebViewBridge.h in CopyFiles */,
4136724A1BEB069E00E9FCEB /* RCTWebViewManager+WebViewManager.h in CopyFiles */,
4136724B1BEB069E00E9FCEB /* webview-bridge-script.js in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
415125421BEB04C40042928F /* libReact-Native-WebView-Bridge.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libReact-Native-WebView-Bridge.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4151254E1BEB05360042928F /* RCTWebView+WebViewBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTWebView+WebViewBridge.h"; sourceTree = SOURCE_ROOT; };
4151254F1BEB05360042928F /* RCTWebView+WebViewBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTWebView+WebViewBridge.m"; sourceTree = SOURCE_ROOT; };
415125501BEB05360042928F /* RCTWebViewManager+WebViewManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "RCTWebViewManager+WebViewManager.h"; sourceTree = SOURCE_ROOT; };
415125511BEB05360042928F /* RCTWebViewManager+WebViewManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "RCTWebViewManager+WebViewManager.m"; sourceTree = SOURCE_ROOT; };
415125521BEB05360042928F /* webview-bridge-script.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = "webview-bridge-script.js"; sourceTree = SOURCE_ROOT; };
4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libReact-Native-Webview-Bridge.a"; sourceTree = BUILT_PRODUCTS_DIR; };
4114DC581C187CCB003CD988 /* RCTWebViewBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewBridge.h; sourceTree = SOURCE_ROOT; };
4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewBridge.m; sourceTree = SOURCE_ROOT; };
4114DC5A1C187CCB003CD988 /* RCTWebViewBridgeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewBridgeManager.h; sourceTree = SOURCE_ROOT; };
4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewBridgeManager.m; sourceTree = SOURCE_ROOT; };
4114DC5F1C187CE4003CD988 /* webviewbridge.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = webviewbridge.js; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
4151253F1BEB04C40042928F /* Frameworks */ = {
4114DC491C187C3A003CD988 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -49,99 +43,108 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
415125391BEB04C40042928F = {
4114DC431C187C3A003CD988 = {
isa = PBXGroup;
children = (
415125441BEB04C40042928F /* React-Native-WebView-Bridge */,
415125431BEB04C40042928F /* Products */,
4114DC4E1C187C3A003CD988 /* React-Native-Webview-Bridge */,
4114DC5E1C187CE4003CD988 /* scripts */,
4114DC4D1C187C3A003CD988 /* Products */,
);
sourceTree = "<group>";
};
415125431BEB04C40042928F /* Products */ = {
4114DC4D1C187C3A003CD988 /* Products */ = {
isa = PBXGroup;
children = (
415125421BEB04C40042928F /* libReact-Native-WebView-Bridge.a */,
4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */,
);
name = Products;
sourceTree = "<group>";
};
415125441BEB04C40042928F /* React-Native-WebView-Bridge */ = {
4114DC4E1C187C3A003CD988 /* React-Native-Webview-Bridge */ = {
isa = PBXGroup;
children = (
4151254E1BEB05360042928F /* RCTWebView+WebViewBridge.h */,
4151254F1BEB05360042928F /* RCTWebView+WebViewBridge.m */,
415125501BEB05360042928F /* RCTWebViewManager+WebViewManager.h */,
415125511BEB05360042928F /* RCTWebViewManager+WebViewManager.m */,
415125521BEB05360042928F /* webview-bridge-script.js */,
4114DC581C187CCB003CD988 /* RCTWebViewBridge.h */,
4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */,
4114DC5A1C187CCB003CD988 /* RCTWebViewBridgeManager.h */,
4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */,
);
path = "React-Native-WebView-Bridge";
path = "React-Native-Webview-Bridge";
sourceTree = "<group>";
};
4114DC5E1C187CE4003CD988 /* scripts */ = {
isa = PBXGroup;
children = (
4114DC5F1C187CE4003CD988 /* webviewbridge.js */,
);
name = scripts;
path = ../scripts;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
415125411BEB04C40042928F /* React-Native-WebView-Bridge */ = {
4114DC4B1C187C3A003CD988 /* React-Native-Webview-Bridge */ = {
isa = PBXNativeTarget;
buildConfigurationList = 4151254B1BEB04C40042928F /* Build configuration list for PBXNativeTarget "React-Native-WebView-Bridge" */;
buildConfigurationList = 4114DC551C187C3A003CD988 /* Build configuration list for PBXNativeTarget "React-Native-Webview-Bridge" */;
buildPhases = (
4151253E1BEB04C40042928F /* Sources */,
4151253F1BEB04C40042928F /* Frameworks */,
415125401BEB04C40042928F /* CopyFiles */,
4114DC481C187C3A003CD988 /* Sources */,
4114DC491C187C3A003CD988 /* Frameworks */,
4114DC4A1C187C3A003CD988 /* CopyFiles */,
);
buildRules = (
);
dependencies = (
);
name = "React-Native-WebView-Bridge";
productName = "React-Native-WebView-Bridge";
productReference = 415125421BEB04C40042928F /* libReact-Native-WebView-Bridge.a */;
name = "React-Native-Webview-Bridge";
productName = "React-Native-Webview-Bridge";
productReference = 4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */;
productType = "com.apple.product-type.library.static";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
4151253A1BEB04C40042928F /* Project object */ = {
4114DC441C187C3A003CD988 /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0710;
ORGANIZATIONNAME = "Ali Najafizadeh";
ORGANIZATIONNAME = alinz;
TargetAttributes = {
415125411BEB04C40042928F = {
CreatedOnToolsVersion = 7.1;
4114DC4B1C187C3A003CD988 = {
CreatedOnToolsVersion = 7.1.1;
};
};
};
buildConfigurationList = 4151253D1BEB04C40042928F /* Build configuration list for PBXProject "React-Native-WebView-Bridge" */;
buildConfigurationList = 4114DC471C187C3A003CD988 /* Build configuration list for PBXProject "React-Native-Webview-Bridge" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = 415125391BEB04C40042928F;
productRefGroup = 415125431BEB04C40042928F /* Products */;
mainGroup = 4114DC431C187C3A003CD988;
productRefGroup = 4114DC4D1C187C3A003CD988 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
415125411BEB04C40042928F /* React-Native-WebView-Bridge */,
4114DC4B1C187C3A003CD988 /* React-Native-Webview-Bridge */,
);
};
/* End PBXProject section */
/* Begin PBXSourcesBuildPhase section */
4151253E1BEB04C40042928F /* Sources */ = {
4114DC481C187C3A003CD988 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
415125531BEB05360042928F /* RCTWebView+WebViewBridge.m in Sources */,
415125541BEB05360042928F /* RCTWebViewManager+WebViewManager.m in Sources */,
4114DC5D1C187CCB003CD988 /* RCTWebViewBridgeManager.m in Sources */,
4114DC5C1C187CCB003CD988 /* RCTWebViewBridge.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin XCBuildConfiguration section */
415125491BEB04C40042928F /* Debug */ = {
4114DC531C187C3A003CD988 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@@ -184,7 +187,7 @@
};
name = Debug;
};
4151254A1BEB04C40042928F /* Release */ = {
4114DC541C187C3A003CD988 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
@@ -221,13 +224,13 @@
};
name = Release;
};
4151254C1BEB04C40042928F /* Debug */ = {
4114DC561C187C3A003CD988 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../../react-native/React/**",
"$(inhereted)",
"$(SRCROOT)/../../../node_module/React/**",
"$(SRCROOT)/../../../node_modules/react-native/React/**",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -235,13 +238,13 @@
};
name = Debug;
};
4151254D1BEB04C40042928F /* Release */ = {
4114DC571C187C3A003CD988 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
HEADER_SEARCH_PATHS = (
"$(inherited)",
"$(SRCROOT)/../../../React/**",
"$(SRCROOT)/../../../react-native/React/**",
"$(inhereted)",
"$(SRCROOT)/../../../node_module/React/**",
"$(SRCROOT)/../../../node_modules/react-native/React/**",
);
OTHER_LDFLAGS = "-ObjC";
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -252,25 +255,25 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
4151253D1BEB04C40042928F /* Build configuration list for PBXProject "React-Native-WebView-Bridge" */ = {
4114DC471C187C3A003CD988 /* Build configuration list for PBXProject "React-Native-Webview-Bridge" */ = {
isa = XCConfigurationList;
buildConfigurations = (
415125491BEB04C40042928F /* Debug */,
4151254A1BEB04C40042928F /* Release */,
4114DC531C187C3A003CD988 /* Debug */,
4114DC541C187C3A003CD988 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
4151254B1BEB04C40042928F /* Build configuration list for PBXNativeTarget "React-Native-WebView-Bridge" */ = {
4114DC551C187C3A003CD988 /* Build configuration list for PBXNativeTarget "React-Native-Webview-Bridge" */ = {
isa = XCConfigurationList;
buildConfigurations = (
4151254C1BEB04C40042928F /* Debug */,
4151254D1BEB04C40042928F /* Release */,
4114DC561C187C3A003CD988 /* Debug */,
4114DC571C187C3A003CD988 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 4151253A1BEB04C40042928F /* Project object */;
rootObject = 4114DC441C187C3A003CD988 /* Project object */;
}
@@ -2,6 +2,6 @@
<Workspace
version = "1.0">
<FileRef
location = "self:React-Native-WebView-Bridge.xcodeproj">
location = "self:React-Native-Webview-Bridge.xcodeproj">
</FileRef>
</Workspace>
@@ -14,10 +14,10 @@
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "415125411BEB04C40042928F"
BuildableName = "libReact-Native-WebView-Bridge.a"
BlueprintName = "React-Native-WebView-Bridge"
ReferencedContainer = "container:React-Native-WebView-Bridge.xcodeproj">
BlueprintIdentifier = "4114DC4B1C187C3A003CD988"
BuildableName = "libReact-Native-Webview-Bridge.a"
BlueprintName = "React-Native-Webview-Bridge"
ReferencedContainer = "container:React-Native-Webview-Bridge.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
@@ -45,10 +45,10 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "415125411BEB04C40042928F"
BuildableName = "libReact-Native-WebView-Bridge.a"
BlueprintName = "React-Native-WebView-Bridge"
ReferencedContainer = "container:React-Native-WebView-Bridge.xcodeproj">
BlueprintIdentifier = "4114DC4B1C187C3A003CD988"
BuildableName = "libReact-Native-Webview-Bridge.a"
BlueprintName = "React-Native-Webview-Bridge"
ReferencedContainer = "container:React-Native-Webview-Bridge.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
@@ -63,10 +63,10 @@
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "415125411BEB04C40042928F"
BuildableName = "libReact-Native-WebView-Bridge.a"
BlueprintName = "React-Native-WebView-Bridge"
ReferencedContainer = "container:React-Native-WebView-Bridge.xcodeproj">
BlueprintIdentifier = "4114DC4B1C187C3A003CD988"
BuildableName = "libReact-Native-Webview-Bridge.a"
BlueprintName = "React-Native-Webview-Bridge"
ReferencedContainer = "container:React-Native-Webview-Bridge.xcodeproj">
</BuildableReference>
</MacroExpansion>
</ProfileAction>
@@ -4,7 +4,7 @@
<dict>
<key>SchemeUserState</key>
<dict>
<key>React-Native-WebView-Bridge.xcscheme</key>
<key>React-Native-Webview-Bridge.xcscheme</key>
<dict>
<key>orderHint</key>
<integer>0</integer>
@@ -12,7 +12,7 @@
</dict>
<key>SuppressBuildableAutocreation</key>
<dict>
<key>415125411BEB04C40042928F</key>
<key>4114DC4B1C187C3A003CD988</key>
<dict>
<key>primary</key>
<true/>
-106
View File
@@ -1,106 +0,0 @@
'use strict';
var React = require('react-native');
var {
WebView,
NativeModules: {
WebViewManager
}
} = React;
class WebViewBridge extends WebView {
constructor(props) {
super(props);
this.handlerId = 0;
}
/*
* call the callback with handler id
*/
getWebViewBridgeHandler(fn) {
//this method defines in WebView component.
//in react-native 0.6 and below, getWebWiewHandle
//in react-native 0.7 and above getWebViewHandle
var handler = this.getWebWiewHandle || this.getWebViewHandle;
if (this.handlerId) {
fn(this.handlerId);
} else {
// this is a hack to get the handleId correctly and
// also avoid race condition.
setTimeout(() => {
this.handlerId = handler();
fn(this.handlerId);
}, 0);
}
}
/*
* inject script into webView
*/
injectBridgeScript() {
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.injectBridgeScript(handlerId);
});
}
onMessage(cb) {
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.onMessage(handlerId, (messages) => {
messages.forEach((message) => {
cb(message);
});
//re-register the callback again
this.onMessage(cb);
});
});
}
evalScript(value) {
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.eval(handlerId, value);
});
}
send(message) {
if (typeof message !== 'string') {
message = JSON.stringify(message);
}
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.send(handlerId, message);
});
}
print() {
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.print(handlerId);
});
}
componentDidMount() {
if (super.componentDidMount) {
super.componentDidMount();
}
//setup the internal variables of webview bridge
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.bridgeSetup(handlerId);
});
}
componentWillUnmount() {
if (super.componentWillUnmount) {
super.componentWillMount();
}
//removed the internal variables from objective-c side related to
//handler id
this.getWebViewBridgeHandler((handlerId) => {
WebViewManager.callbackCleanup(handlerId);
this.handlerId = 0;
});
}
}
module.exports = WebViewBridge;
-27
View File
@@ -1,27 +0,0 @@
//
// RCTWebView+WebViewBridge.h
// Sample2
//
// Created by Ali Najafizadeh on 2015-07-10.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "RCTWebView.h"
#import "RCTBridgeModule.h"
#import "RCTEventDispatcher.h"
@interface RCTWebView (WebViewBridge)
- (void)setEvetnDispatcher:(RCTEventDispatcher *)eventDispatcher;
- (void)injectBridgeScript:(NSNumber*)reactTag;
- (void)print;
- (void)eval:(NSString *) value;
- (void)bridgeSetup;
- (void)send:(NSString*)message;
- (void)callbackCleanup:(NSNumber *)reactTag;
- (void)onMessageCallback:(RCTResponseSenderBlock)callback withReactTag:(NSNumber *)reactTag;
//we are making this method visible to public. [Can't find any other way]
- (NSMutableDictionary *)baseEvent;
@end
-165
View File
@@ -1,165 +0,0 @@
//
// RCTWebView+WebViewBridge.m
// Sample2
//
// Created by Ali Najafizadeh on 2015-07-10.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "RCTWebView+WebViewBridge.h"
#import "RCTEventDispatcher.h"
#import "UIView+React.h"
static NSString *const RCTJSAJAXScheme = @"react-ajax";
static NSString *const RNWBSchema = @"rnwb";
//since category won't let us add variables to class, we need a static map
//to store information about our callbacks. These callbacks can be refereced by reactTag ids.
static NSMutableDictionary * callbackMap;
static dispatch_queue_t serialQueue;
@implementation RCTWebView (WebViewBridge)
RCTEventDispatcher *_eventDispatcher;
- (void)setEvetnDispatcher:(RCTEventDispatcher *)eventDispatcher{
_eventDispatcher = eventDispatcher;
}
- (void) bridgeSetup {
static dispatch_once_t onceQueue;
dispatch_once(&onceQueue, ^{
callbackMap = [[NSMutableDictionary alloc] init];
serialQueue = dispatch_queue_create("react-native-webview-bridge", NULL);
});
}
//ref http://stackoverflow.com/questions/6544733/ios-air-print-for-uiwebview
- (void) print {
UIPrintInteractionController *pic = [UIPrintInteractionController sharedPrintController];
//pic.delegate = self;
UIPrintInfo *printInfo = [UIPrintInfo printInfo];
printInfo.outputType = UIPrintInfoOutputGeneral;
printInfo.jobName = @"print-job";
printInfo.duplex = UIPrintInfoDuplexLongEdge;
pic.printInfo = printInfo;
pic.showsPageRange = YES;
UIWebView *webview = [self valueForKey:@"_webView"];
UIViewPrintFormatter *formatter = [webview viewPrintFormatter];
pic.printFormatter = formatter;
void (^completionHandler)(UIPrintInteractionController *, BOOL, NSError *) =
^(UIPrintInteractionController *printController, BOOL completed, NSError *error) {
if (!completed && error) {
NSLog(@"Printing could not complete because of error: %@", error);
}
};
[pic presentAnimated:YES completionHandler:completionHandler];
}
- (void)send:(NSString*)message {
UIWebView* _webView = [self valueForKey:@"_webView"];
NSString *command = [NSString stringWithFormat: @"WebViewBridge.onMessage('%@');", message];
[_webView stringByEvaluatingJavaScriptFromString:command];
}
- (void) callbackCleanup:(NSNumber *)reactTag {
[callbackMap removeObjectForKey:reactTag];
}
- (void)onMessageCallback:(RCTResponseSenderBlock)callback withReactTag:(NSNumber *)reactTag {
dispatch_sync(serialQueue, ^{
[callbackMap setObject:callback forKeyedSubscript:reactTag];
});
}
- (void)eval:(NSString *) value {
//access to provate variable
UIWebView* _webView = [self valueForKey:@"_webView"];
[_webView stringByEvaluatingJavaScriptFromString:value];
//NSLog(@"Called Eval %@", value);
}
- (BOOL) isSignalTriggered:(UIWebView *)webView withRequest:(NSURLRequest *)request {
NSURL *URL = [request URL];
if ([[URL scheme] isEqualToString:RNWBSchema]) {
// parse the rest of the URL object and execute functions
NSString* message = [webView stringByEvaluatingJavaScriptFromString:@"WebViewBridge._fetch()"];
NSArray* temp = [self __jsonParseArray: message];
RCTResponseSenderBlock callbackHandler = (RCTResponseSenderBlock)[callbackMap objectForKey:temp[0]];
NSArray* messageArray = [self removeObjectFromArray:temp withIndex:0];
callbackHandler(@[messageArray]);
return YES;
}
return NO;
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
//we need to check whether it's coming from our request schema
if ([self isSignalTriggered:webView withRequest:request]) {
return NO;
}
// We have this check to filter out iframe requests and whatnot
BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL];
if (isTopFrame) {
NSMutableDictionary *event = [self baseEvent];
[event addEntriesFromDictionary: @{
@"target": self.reactTag,
@"url": [request.URL absoluteString],
@"navigationType": @(navigationType)
}];
[_eventDispatcher sendInputEventWithName:@"topLoadingStart" body:event];
}
// AJAX handler
return ![request.URL.scheme isEqualToString:RCTJSAJAXScheme];
}
- (BOOL)isWebViewBridgeInstantiated:(UIWebView *)webView {
return [[webView stringByEvaluatingJavaScriptFromString:@"typeof WebViewBridge == 'object'"] isEqualToString:@"true"];
}
- (void)injectBridgeScript:(NSNumber*)reactTag {
UIWebView* _webView = [self valueForKey:@"_webView"];
if (![self isWebViewBridgeInstantiated:_webView]) {
NSBundle *bundle = [NSBundle mainBundle];
NSString *filePath = [bundle pathForResource:@"webview-bridge-script" ofType:@"js"];
NSString *handlerId = [NSString stringWithFormat: @"var webViewBridgeHandlerId = %@;", reactTag];
NSString *js = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
js = [js stringByReplacingOccurrencesOfString:@"var webViewBridgeHandlerId = 0;"
withString:handlerId];
[_webView stringByEvaluatingJavaScriptFromString:js];
}
}
- (NSArray*)__jsonParseArray:(NSString *)messageJSON {
return [NSJSONSerialization JSONObjectWithData:[messageJSON dataUsingEncoding:NSUTF8StringEncoding]
options:NSJSONReadingAllowFragments
error:nil];
}
- (NSString *)__jsonStringify:(id)message {
return [[NSString alloc] initWithData:[NSJSONSerialization dataWithJSONObject:message
options:0
error:nil]
encoding:NSUTF8StringEncoding];
}
-(NSArray *) removeObjectFromArray:(NSArray *) array withIndex:(NSInteger) index {
NSMutableArray *modifyableArray = [[NSMutableArray alloc] initWithArray:array];
[modifyableArray removeObjectAtIndex:index];
return [[NSArray alloc] initWithArray:modifyableArray];
}
@end
@@ -1,13 +0,0 @@
//
// RCTWebViewManager+WebViewManager.h
// Sample2
//
// Created by Ali Najafizadeh on 2015-07-10.
// Copyright (c) 2015 Facebook. All rights reserved.
//
#import "RCTWebViewManager.h"
@interface RCTWebViewManager (WebViewManager)
@end
-105
View File
@@ -1,105 +0,0 @@
//
// RCTWebViewManager+WebViewManager.m
// Sample2
//
// Created by Ali Najafizadeh on 2015-07-10.
#import "RCTWebViewManager+WebViewManager.h"
#import "RCTBridge.h"
#import "RCTSparseArray.h"
#import "RCTUIManager.h"
#import "RCTWebView.h"
#import "RCTWebView+WebViewBridge.h"
@implementation RCTWebViewManager (WebViewManager)
//NOTE
//DO not include RCT_EXPORT_MODULE() here because RCTWebViewManager already has it and
//we are using category feature in objective-c
RCT_EXPORT_METHOD(bridgeSetup:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
[view setEvetnDispatcher:self.bridge.eventDispatcher];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view bridgeSetup];
}];
}
RCT_EXPORT_METHOD(callbackCleanup:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view callbackCleanup:reactTag];
}];
}
RCT_EXPORT_METHOD(onMessage:(nonnull NSNumber *)reactTag
withCallback:(RCTResponseSenderBlock)callback)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view onMessageCallback:callback withReactTag:reactTag];
}];
}
RCT_EXPORT_METHOD(send:(nonnull NSNumber *)reactTag
value:(NSString*)message)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view send:message];
}];
}
RCT_EXPORT_METHOD(eval:(nonnull NSNumber *)reactTag
value:(NSString*)value)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view eval:value];
}];
}
RCT_EXPORT_METHOD(injectBridgeScript:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view injectBridgeScript: reactTag];
}];
}
RCT_EXPORT_METHOD(print:(nonnull NSNumber *)reactTag)
{
[self.bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
RCTWebView *view = viewRegistry[reactTag];
if (![view isKindOfClass:[RCTWebView class]]) {
RCTLogMustFix(@"Invalid view returned from registry, expecting RKWebView, got: %@", view);
}
[view print];
}];
}
@end
-42
View File
@@ -1,42 +0,0 @@
(function() {
'use strict';
//this variable will be set during the injection by objective-c
//we need to know the handlerId in order to locate the callback properly.
var webViewBridgeHandlerId = 0;
var doc = document;
var WebViewBridge = {};
var RNWBSchema = "rnwb";
var queue = [];
var inProcess = false;
var customEvent = doc.createEvent('Event');
function noop() {}
WebViewBridge = {
//do not call _fetch directly. this is for internal use
_fetch: function () {
var message;
queue.unshift(webViewBridgeHandlerId);
message = JSON.stringify(queue);
queue = [];
inProcess = false;
return message;
},
send: function (value) {
queue.push(value);
if (!inProcess) {
inProcess = true;
//signal the objective-c that there is a message in the queue
window.location = RNWBSchema + '://message' + new Date().getTime();
}
},
onMessage: noop
};
window.WebViewBridge = WebViewBridge;
customEvent.initEvent('WebViewBridge', true, true);
doc.dispatchEvent(customEvent);
}());
+7 -6
View File
@@ -1,15 +1,12 @@
{
"name": "react-native-webview-bridge",
"version": "0.4.0",
"version": "0.16.0",
"description": "React Native WebView Javascript Bridge",
"main": "jsx/WebViewBridge.js",
"main": "webview-bridge",
"directories": {
"example": "example",
"lib": "lib"
},
"peerDependencies": {
"react-native": ">= 0.10.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
@@ -28,5 +25,9 @@
"bugs": {
"url": "https://github.com/alinz/react-native-webview-bridge/issues"
},
"homepage": "https://github.com/alinz/react-native-webview-bridge"
"homepage": "https://github.com/alinz/react-native-webview-bridge",
"dependencies": {
"invariant": "2.2.0",
"keymirror": "0.1.1"
}
}
+66
View File
@@ -0,0 +1,66 @@
(function (window) {
'use strict';
//Make sure that if WebViewBridge already in scope we don't override it.
if (window.WebViewBridge) {
return;
}
var RNWBSchema = 'wvb';
var sendQueue = [];
var receiveQueue = [];
function callFunc(func, message) {
if ('function' === typeof func) {
func(message);
}
}
function signalNative() {
window.location = RNWBSchema + '://message' + new Date().getTime();
}
//I made the private function ugly signiture so user doesn't called them accidently.
//if you do, then I have nothing to say. :(
var WebViewBridge = {
//this function will be called by native side to push a new message
//to webview.
__push__: function (message) {
receiveQueue.push(message);
//reason I need this setTmeout is to return this function as fast as
//possible to release the native side thread.
setTimeout(function () {
var message = receiveQueue.pop();
callFunc(WebViewBridge.onMessage, message);
}, 15); //this magic number is just a random small value. I don't like 0.
},
__fetch__: function () {
//since our sendQueue array only contains string, and our connection to native
//can only accept string, we need to convert array of strings into single string.
var messages = JSON.stringify(sendQueue);
//we make sure that sendQueue is resets
sendQueue = [];
//return the messages back to native side.
return messages;
},
//make sure message is string. because only string can be sent to native,
//if you don't pass it as string, onError function will be called.
send: function (message) {
if ('string' !== typeof message) {
callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string");
return;
}
//we queue the messages to make sure that native can collects all of them in one shot.
sendQueue.push(message);
//signal the objective-c that there is a message in the queue
signalNative();
},
onMessage: null,
onError: null
};
window.WebViewBridge = WebViewBridge;
}(window));
+334
View File
@@ -0,0 +1,334 @@
/**
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz)
* All rights reserved
*
* @providesModule WebViewBridge
* @flow
*/
'use strict';
var React = require('react-native');
var invariant = require('invariant');
var keyMirror = require('keymirror');
var {
ActivityIndicatorIOS,
EdgeInsetsPropType,
StyleSheet,
Text,
View,
requireNativeComponent,
PropTypes,
NativeModules: {
WebViewBridgeManager
}
} = React;
var BGWASH = 'rgba(255,255,255,0.8)';
var RCT_WEBVIEW_BRIDGE_REF = 'webviewbridge';
var WebViewBridgeState = keyMirror({
IDLE: null,
LOADING: null,
ERROR: null,
});
var NavigationType = {
click: WebViewBridgeManager.NavigationType.LinkClicked,
formsubmit: WebViewBridgeManager.NavigationType.FormSubmitted,
backforward: WebViewBridgeManager.NavigationType.BackForward,
reload: WebViewBridgeManager.NavigationType.Reload,
formresubmit: WebViewBridgeManager.NavigationType.FormResubmitted,
other: WebViewBridgeManager.NavigationType.Other,
};
var JSNavigationScheme = WebViewBridgeManager.JSNavigationScheme;
type ErrorEvent = {
domain: any;
code: any;
description: any;
}
type Event = Object;
var defaultRenderLoading = () => (
<View style={styles.loadingView}>
<ActivityIndicatorIOS />
</View>
);
var defaultRenderError = (errorDomain, errorCode, errorDesc) => (
<View style={styles.errorContainer}>
<Text style={styles.errorTextTitle}>
Error loading page
</Text>
<Text style={styles.errorText}>
{'Domain: ' + errorDomain}
</Text>
<Text style={styles.errorText}>
{'Error Code: ' + errorCode}
</Text>
<Text style={styles.errorText}>
{'Description: ' + errorDesc}
</Text>
</View>
);
/**
* Renders a native WebView.
*
* Note that WebView is only supported on iOS for now,
* see https://facebook.github.io/react-native/docs/known-issues.html
*/
var WebViewBridge = React.createClass({
statics: {
JSNavigationScheme: JSNavigationScheme,
NavigationType: NavigationType,
},
propTypes: {
...View.propTypes,
url: PropTypes.string,
html: PropTypes.string,
renderError: PropTypes.func, // view to show if there's an error
renderLoading: PropTypes.func, // loading indicator to show
bounces: PropTypes.bool,
scrollEnabled: PropTypes.bool,
automaticallyAdjustContentInsets: PropTypes.bool,
contentInset: EdgeInsetsPropType,
onNavigationStateChange: PropTypes.func,
startInLoadingState: PropTypes.bool, // force WebView to show loadingView on first load
style: View.propTypes.style,
/**
* Used for android only, JS is enabled by default for WebView on iOS
* @platform android
*/
javaScriptEnabledAndroid: PropTypes.bool,
/**
* Sets the JS to be injected when the webpage loads.
*/
injectedJavaScript: PropTypes.string,
/**
* Sets whether the webpage scales to fit the view and the user can change the scale.
* @platform ios
*/
scalesPageToFit: PropTypes.bool,
/**
* Allows custom handling of any webview requests by a JS handler. Return true
* or false from this method to continue loading the request.
* @platform ios
*/
onShouldStartLoadWithRequest: PropTypes.func,
/**
* Determines whether HTML5 videos play inline or use the native full-screen
* controller.
* default value `false`
* **NOTE** : "In order for video to play inline, not only does this
* property need to be set to true, but the video element in the HTML
* document must also include the webkit-playsinline attribute."
* @platform ios
*/
allowsInlineMediaPlayback: PropTypes.bool,
/**
* Will be called once the message is being sent from webview
*/
onBridgeMessage: PropTypes.func,
},
getInitialState: function() {
return {
viewState: WebViewBridgeState.IDLE,
lastErrorEvent: (null: ?ErrorEvent),
startInLoadingState: true,
};
},
componentWillMount: function() {
if (this.props.startInLoadingState) {
this.setState({viewState: WebViewBridgeState.LOADING});
}
},
render: function() {
var otherView = null;
if (this.state.viewState === WebViewBridgeState.LOADING) {
otherView = (this.props.renderLoading || defaultRenderLoading)();
} else if (this.state.viewState === WebViewBridgeState.ERROR) {
var errorEvent = this.state.lastErrorEvent;
invariant(
errorEvent != null,
'lastErrorEvent expected to be non-null'
);
otherView = (this.props.renderError || defaultRenderError)(
errorEvent.domain,
errorEvent.code,
errorEvent.description
);
} else if (this.state.viewState !== WebViewBridgeState.IDLE) {
console.error(
'RCTWebViewBridge invalid state encountered: ' + this.state.loading
);
}
var webViewBridgeStyles = [styles.container, styles.webViewBridge, this.props.style];
if (this.state.viewState === WebViewBridgeState.LOADING ||
this.state.viewState === WebViewBridgeState.ERROR) {
// if we're in either LOADING or ERROR states, don't show the webView
webViewBridgeStyles.push(styles.hidden);
}
var onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => {
var shouldStart = this.props.onShouldStartLoadWithRequest &&
this.props.onShouldStartLoadWithRequest(event.nativeEvent);
WebViewBridgeManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier);
});
var onBridgeMessage = (event: Event) => {
var onBridgeMessageCallback = this.props.onBridgeMessage;
if (onBridgeMessageCallback) {
const messages = event.nativeEvent.messages;
messages.forEach((message) => {
onBridgeMessageCallback(message);
});
}
};
var webViewBridge =
<RCTWebViewBridge
ref={RCT_WEBVIEW_BRIDGE_REF}
key="webViewBridgeKey"
style={webViewBridgeStyles}
url={this.props.url}
html={this.props.html}
injectedJavaScript={this.props.injectedJavaScript}
bounces={this.props.bounces}
scrollEnabled={this.props.scrollEnabled}
contentInset={this.props.contentInset}
automaticallyAdjustContentInsets={this.props.automaticallyAdjustContentInsets}
onLoadingStart={this.onLoadingStart}
onLoadingFinish={this.onLoadingFinish}
onLoadingError={this.onLoadingError}
onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
scalesPageToFit={this.props.scalesPageToFit}
allowsInlineMediaPlayback={this.props.allowsInlineMediaPlayback}
onBridgeMessage={onBridgeMessage}
/>;
return (
<View style={styles.container}>
{webViewBridge}
{otherView}
</View>
);
},
goForward: function() {
WebViewBridgeManager.goForward(this.getWebViewBridgeHandle());
},
goBack: function() {
WebViewBridgeManager.goBack(this.getWebViewBridgeHandle());
},
reload: function() {
WebViewBridgeManager.reload(this.getWebViewBridgeHandle());
},
sendToBridge: function (message) {
WebViewBridgeManager.sendToBridge(this.getWebViewBridgeHandle(), message);
},
/**
* We return an event with a bunch of fields including:
* url, title, loading, canGoBack, canGoForward
*/
updateNavigationState: function(event: Event) {
if (this.props.onNavigationStateChange) {
this.props.onNavigationStateChange(event.nativeEvent);
}
},
getWebViewBridgeHandle: function(): any {
return React.findNodeHandle(this.refs[RCT_WEBVIEW_BRIDGE_REF]);
},
onLoadingStart: function(event: Event) {
this.updateNavigationState(event);
},
onLoadingError: function(event: Event) {
event.persist(); // persist this event because we need to store it
console.warn('Encountered an error loading page', event.nativeEvent);
this.setState({
lastErrorEvent: event.nativeEvent,
viewState: WebViewBridgeState.ERROR
});
},
onLoadingFinish: function(event: Event) {
this.setState({
viewState: WebViewBridgeState.IDLE,
});
this.updateNavigationState(event);
},
});
var RCTWebViewBridge = requireNativeComponent('RCTWebViewBridge', WebViewBridge, {
nativeOnly: {
onLoadingStart: true,
onLoadingError: true,
onLoadingFinish: true,
},
});
var styles = StyleSheet.create({
container: {
flex: 1,
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: BGWASH,
},
errorText: {
fontSize: 14,
textAlign: 'center',
marginBottom: 2,
},
errorTextTitle: {
fontSize: 15,
fontWeight: '500',
marginBottom: 10,
},
hidden: {
height: 0,
flex: 0, // disable 'flex:1' when hiding a View
},
loadingView: {
backgroundColor: BGWASH,
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
webViewBridge: {
backgroundColor: '#ffffff',
}
});
module.exports = WebViewBridge;