diff --git a/react-native-webview-bridge/.gitignore b/react-native-webview-bridge/.gitignore new file mode 100644 index 00000000..fd4f2b06 --- /dev/null +++ b/react-native-webview-bridge/.gitignore @@ -0,0 +1,2 @@ +node_modules +.DS_Store diff --git a/react-native-webview-bridge/LICENSE b/react-native-webview-bridge/LICENSE new file mode 100644 index 00000000..8793c67c --- /dev/null +++ b/react-native-webview-bridge/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2015 Ali Najafizadeh + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/react-native-webview-bridge/README.md b/react-native-webview-bridge/README.md new file mode 100644 index 00000000..5daf55c4 --- /dev/null +++ b/react-native-webview-bridge/README.md @@ -0,0 +1,181 @@ +# Looking for Maintainer(s) +I have been really busy and I can't continue maintaing this repo alone. I would like to ask if anyone wants to help I can grant a permission both on github and npm to start merging a lot of request and dealing with issues. + +Please send me your github and npm id so I can add you in. + +Thanks! + + +## Please take a look at this [issue](https://github.com/alinz/react-native-webview-bridge/issues/109) first + +# React Native WebView Javascript Bridge +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.20 and above**, the bridge is fully functional. + + + +## Installation + +In order to use this extension, you have to do the following steps: + +in your react-native project, run `npm install react-native-webview-bridge --save` + +### iOS + +1. go to xcode's `Project Navigator` tab +

+ +

+2. right click on `Libraries` +3. select `Add Files to ...` option +

+ +

+4. navigate to `node_modules/react-native-webview-bridge/ios` and add `React-Native-Webview-Bridge.xcodeproj` folder +

+ +

+5. on project `Project Navigator` tab, click on your project's name and select Target's name and from there click on `Build Phases` +

+ +

+6. expand `Link Binary With Libraries` and click `+` sign to add a new one. +7. select `libReact-Native-Webviwe-Bridge.a` and click `Add` button. +

+ +

+8. clean compile to make sure your project can compile and build. + +### Android + +1. add the following import to `MainApplication.java` (`MainActivity.java` if RN < 0.29) of your application + +```java +import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage; +``` + +2. add the following code to add the package to `MainApplication.java`` (`MainActivity.java` if RN < 0.29) + +```java +protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new WebViewBridgePackage() //<- this + ); + } +``` + +3. add the following codes to your `android/setting.gradle` + +> you might have multiple 3rd party libraries, make sure that you don't create multiple include. + +``` +include ':app', ':react-native-webview-bridge' +project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') +``` + +4. edit `android/app/build.gradle` and add the following line inside `dependencies` + +``` +compile project(':react-native-webview-bridge') +``` + +5. run `react-native run-android` to see if everything is compilable. + +## Usage + +just import the module with one of your choices way: + +** CommonJS style ** + +```js +var WebViewBridge = require('react-native-webview-bridge'); +``` + +** ES6/ES2015 style ** + +```js +import WebViewBridge from 'react-native-webview-bridge'; +``` + +`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. + +#### sendToBridge(message) +the message must be in string. because this is the only way to send data back and forth between native and webview. + + +#### 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. + +#### allowFileAccessFromFileURLs (Android only) +this is a prop that allows locally loaded pages via file:// to access other file:// resources. Pass-thru to corresponding [setting](https://developer.android.com/reference/android/webkit/WebSettings.html#setAllowFileAccessFromFileURLs(boolean)) in WebView. Default is `false` for Android 4.1 and above. + +#### allowUniversalAccessFromFileURLs (Android only) +this is a prop that allows locally loaded pages via file:// to access resources in any origin. Pass-thru to corresponding [setting](https://developer.android.com/reference/android/webkit/WebSettings.html#setAllowUniversalAccessFromFileURLs(boolean)) in WebView. Default is `false` for Android 4.1 and above. + +## 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 (iOS only) + +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) { + if (message === "hello from react-native") { + WebViewBridge.send("got the message inside webview"); + } + }; + + WebViewBridge.send("hello from webview"); + } + }()); +`; + +var Sample2 = React.createClass({ + onBridgeMessage(message){ + const { webviewbridge } = this.refs; + + switch (message) { + case "hello from webview": + webviewbridge.sendToBridge("hello from react-native"); + break; + case "got the message inside webview": + console.log("we have got a message from webview! yeah"); + break; + } + }, + + render() { + return ( + + ); + } +}); +``` diff --git a/react-native-webview-bridge/android/build.gradle b/react-native-webview-bridge/android/build.gradle new file mode 100644 index 00000000..dfec22af --- /dev/null +++ b/react-native-webview-bridge/android/build.gradle @@ -0,0 +1,34 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:1.1.3' + } +} + +apply plugin: 'com.android.library' + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + minSdkVersion 16 + targetSdkVersion 23 + versionCode 1 + versionName "1.0" + } + lintOptions { + abortOnError false + } +} + +repositories { + mavenCentral() +} + +dependencies { + compile 'com.facebook.react:react-native:0.19.+' +} diff --git a/react-native-webview-bridge/android/src/main/AndroidManifest.xml b/react-native-webview-bridge/android/src/main/AndroidManifest.xml new file mode 100644 index 00000000..84054d58 --- /dev/null +++ b/react-native-webview-bridge/android/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + diff --git a/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/JavascriptBridge.java b/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/JavascriptBridge.java new file mode 100644 index 00000000..2ad37116 --- /dev/null +++ b/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/JavascriptBridge.java @@ -0,0 +1,29 @@ +package com.github.alinz.reactnativewebviewbridge; + +import android.webkit.JavascriptInterface; +import android.webkit.WebView; + +import com.facebook.react.bridge.Arguments; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.WritableMap; +import com.facebook.react.modules.core.DeviceEventManagerModule; +import com.facebook.react.uimanager.events.RCTEventEmitter; + +class JavascriptBridge { + private WebView webView; + + public JavascriptBridge(WebView webView) { + this.webView = webView; + } + + @JavascriptInterface + public void send(String message) { + WritableMap event = Arguments.createMap(); + event.putString("message", message); + ReactContext reactContext = (ReactContext) this.webView.getContext(); + reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( + this.webView.getId(), + "topChange", + event); + } +} \ No newline at end of file diff --git a/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java b/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java new file mode 100644 index 00000000..c1250163 --- /dev/null +++ b/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java @@ -0,0 +1,78 @@ +package com.github.alinz.reactnativewebviewbridge; + +import android.webkit.WebView; + +import com.facebook.react.bridge.ReadableArray; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.views.webview.ReactWebViewManager; +import com.facebook.react.uimanager.annotations.ReactProp; + +import java.util.ArrayList; +import java.util.Map; + +import javax.annotation.Nullable; + +public class WebViewBridgeManager extends ReactWebViewManager { + private static final String REACT_CLASS = "RCTWebViewBridge"; + + public static final int COMMAND_SEND_TO_BRIDGE = 101; + + @Override + public String getName() { + return REACT_CLASS; + } + + @Override + public + @Nullable + Map getCommandsMap() { + Map commandsMap = super.getCommandsMap(); + + commandsMap.put("sendToBridge", COMMAND_SEND_TO_BRIDGE); + + return commandsMap; + } + + @Override + protected WebView createViewInstance(ThemedReactContext reactContext) { + WebView root = super.createViewInstance(reactContext); + root.addJavascriptInterface(new JavascriptBridge(root), "WebViewBridge"); + return root; + } + + @Override + public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) { + super.receiveCommand(root, commandId, args); + + switch (commandId) { + case COMMAND_SEND_TO_BRIDGE: + sendToBridge(root, args.getString(0)); + break; + default: + //do nothing!!!! + } + } + + private void sendToBridge(WebView root, String message) { + String script = "WebViewBridge.onMessage('" + message + "');"; + WebViewBridgeManager.evaluateJavascript(root, script); + } + + static private void evaluateJavascript(WebView root, String javascript) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { + root.evaluateJavascript(javascript, null); + } else { + root.loadUrl("javascript:" + javascript); + } + } + + @ReactProp(name = "allowFileAccessFromFileURLs") + public void setAllowFileAccessFromFileURLs(WebView root, boolean allows) { + root.getSettings().setAllowFileAccessFromFileURLs(allows); + } + + @ReactProp(name = "allowUniversalAccessFromFileURLs") + public void setAllowUniversalAccessFromFileURLs(WebView root, boolean allows) { + root.getSettings().setAllowUniversalAccessFromFileURLs(allows); + } +} \ No newline at end of file diff --git a/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java b/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java new file mode 100644 index 00000000..1e189c4d --- /dev/null +++ b/react-native-webview-bridge/android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java @@ -0,0 +1,30 @@ +package com.github.alinz.reactnativewebviewbridge; + +import com.facebook.react.ReactPackage; +import com.facebook.react.bridge.JavaScriptModule; +import com.facebook.react.bridge.NativeModule; +import com.facebook.react.bridge.ReactApplicationContext; +import com.facebook.react.uimanager.ViewManager; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class WebViewBridgePackage implements ReactPackage { + @Override + public List createNativeModules(ReactApplicationContext reactApplicationContext) { + return new ArrayList<>(); + } + + @Override + public List createViewManagers(ReactApplicationContext reactApplicationContext) { + return Arrays.asList( + new WebViewBridgeManager() + ); + } + + @Override + public List> createJSModules() { + return Arrays.asList(); + } +} diff --git a/react-native-webview-bridge/doc/assets/01.png b/react-native-webview-bridge/doc/assets/01.png new file mode 100644 index 00000000..465b57cb Binary files /dev/null and b/react-native-webview-bridge/doc/assets/01.png differ diff --git a/react-native-webview-bridge/doc/assets/02.png b/react-native-webview-bridge/doc/assets/02.png new file mode 100644 index 00000000..7b599c5c Binary files /dev/null and b/react-native-webview-bridge/doc/assets/02.png differ diff --git a/react-native-webview-bridge/doc/assets/03.png b/react-native-webview-bridge/doc/assets/03.png new file mode 100644 index 00000000..cf9aa8f9 Binary files /dev/null and b/react-native-webview-bridge/doc/assets/03.png differ diff --git a/react-native-webview-bridge/doc/assets/04.png b/react-native-webview-bridge/doc/assets/04.png new file mode 100644 index 00000000..9c896565 Binary files /dev/null and b/react-native-webview-bridge/doc/assets/04.png differ diff --git a/react-native-webview-bridge/doc/assets/05.png b/react-native-webview-bridge/doc/assets/05.png new file mode 100644 index 00000000..05bf20d3 Binary files /dev/null and b/react-native-webview-bridge/doc/assets/05.png differ diff --git a/react-native-webview-bridge/examples/SampleRN20/.flowconfig b/react-native-webview-bridge/examples/SampleRN20/.flowconfig new file mode 100644 index 00000000..c3edaf94 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/.flowconfig @@ -0,0 +1,65 @@ +[ignore] + +# We fork some components by platform. +.*/*.web.js +.*/*.android.js + +# Some modules have their own node_modules with overlap +.*/node_modules/node-haste/.* + +# 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/lib/Map.js +.*/node_modules/fbjs/lib/Promise.js +.*/node_modules/fbjs/lib/fetch.js +.*/node_modules/fbjs/lib/ExecutionEnvironment.js +.*/node_modules/fbjs/lib/isEmpty.js +.*/node_modules/fbjs/lib/crc32.js +.*/node_modules/fbjs/lib/ErrorUtils.js + +# Flow has a built-in definition for the 'react' module which we prefer to use +# over the currently-untyped source +.*/node_modules/react/react.js +.*/node_modules/react/lib/React.js +.*/node_modules/react/lib/ReactDOM.js + +# Ignore commoner tests +.*/node_modules/commoner/test/.* + +# See https://github.com/facebook/flow/issues/442 +.*/react-tools/node_modules/commoner/lib/reader.js + +# Ignore jest +.*/node_modules/jest-cli/.* + +# Ignore Website +.*/website/.* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js + +[options] +module.system=haste + +munge_underscores=true + +module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\)$' -> 'RelativeImageStub' + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FixMe + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy + +[version] +0.21.0 diff --git a/react-native-webview-bridge/examples/SampleRN20/.gitignore b/react-native-webview-bridge/examples/SampleRN20/.gitignore new file mode 100644 index 00000000..94fc8671 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/.gitignore @@ -0,0 +1,34 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# Android/IJ +# +.idea +.gradle +local.properties + +# node.js +# +node_modules/ +npm-debug.log diff --git a/react-native-webview-bridge/examples/SampleRN20/.watchmanconfig b/react-native-webview-bridge/examples/SampleRN20/.watchmanconfig new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/build.gradle b/react-native-webview-bridge/examples/SampleRN20/android/app/build.gradle new file mode 100644 index 00000000..ca57f4e8 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/build.gradle @@ -0,0 +1,126 @@ +apply plugin: "com.android.application" + +import com.android.build.OutputFile + +/** + * The react.gradle file registers a task for each build variant (e.g. 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, + * + * // whether to bundle JS and assets in another build variant (if configured). + * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants + * // The configuration property is in the format 'bundleIn${productFlavor}${buildType}' + * // bundleInFreeDebug: true, + * // bundleInPaidRelease: true, + * // bundleInBeta: 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" + +/** + * Set this to true to create three separate APKs instead of one: + * - A universal APK that works on all devices + * - An APK that only works on ARM devices + * - An APK that only works on x86 devices + * The advantage is the size of the APK is reduced by about 4MB. + * Upload all the APKs to the Play Store and people will download + * the correct one based on the CPU architecture of their device. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Run Proguard to shrink the Java bytecode in release builds. + */ +def enableProguardInReleaseBuilds = false + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + applicationId "com.samplern20" + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } + splits { + abi { + enable enableSeparateBuildPerCPUArchitecture + universalApk false + reset() + include "armeabi-v7a", "x86" + } + } + buildTypes { + release { + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits + def versionCodes = ["armeabi-v7a":1, "x86":2] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } + } + } +} + +dependencies { + compile fileTree(dir: "libs", include: ["*.jar"]) + compile "com.android.support:appcompat-v7:23.0.1" + compile "com.facebook.react:react-native:0.20.+" + compile project(':react-native-webview-bridge') +} diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/proguard-rules.pro b/react-native-webview-bridge/examples/SampleRN20/android/app/proguard-rules.pro new file mode 100644 index 00000000..7d72e469 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/proguard-rules.pro @@ -0,0 +1,67 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Disabling obfuscation is useful if you collect stack traces from production crashes +# (unless you are using a system that supports de-obfuscate the stack traces). +-dontobfuscate + +# React Native + +# Keep our interfaces so they can be used by other ProGuard rules. +# See http://sourceforge.net/p/proguard/bugs/466/ +-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip +-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters + +# Do not strip any method/class that is annotated with @DoNotStrip +-keep @com.facebook.proguard.annotations.DoNotStrip class * +-keepclassmembers class * { + @com.facebook.proguard.annotations.DoNotStrip *; +} + +-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { + void set*(***); + *** get*(); +} + +-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } +-keep class * extends com.facebook.react.bridge.NativeModule { *; } +-keepclassmembers,includedescriptorclasses class * { native ; } +-keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } +-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } +-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } + +-dontwarn com.facebook.react.** + +# okhttp + +-keepattributes Signature +-keepattributes *Annotation* +-keep class com.squareup.okhttp.** { *; } +-keep interface com.squareup.okhttp.** { *; } +-dontwarn com.squareup.okhttp.** + +# okio + +-keep class sun.misc.Unsafe { *; } +-dontwarn java.nio.file.* +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement +-dontwarn okio.** + +# stetho + +-dontwarn com.facebook.stetho.** diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/react.gradle b/react-native-webview-bridge/examples/SampleRN20/android/app/react.gradle new file mode 100644 index 00000000..4b43bf91 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/react.gradle @@ -0,0 +1,96 @@ +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 inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] + +void runBefore(String dependentTaskName, Task task) { + Task dependentTask = tasks.findByPath(dependentTaskName); + if (dependentTask != null) { + dependentTask.dependsOn task + } +} + +gradle.projectsEvaluated { + // Grab all build types and product flavors + def buildTypes = android.buildTypes.collect { type -> type.name } + def productFlavors = android.productFlavors.collect { flavor -> flavor.name } + + // When no product flavors defined, use empty + if (!productFlavors) productFlavors.add('') + + productFlavors.each { productFlavorName -> + buildTypes.each { buildTypeName -> + // Create variant and source names + def sourceName = "${buildTypeName}" + def targetName = "${sourceName.capitalize()}" + if (productFlavorName) { + sourceName = "${productFlavorName}${targetName}" + } + + // React js bundle directories + def jsBundleDirConfigName = "jsBundleDir${targetName}" + def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?: + file("$buildDir/intermediates/assets/${sourceName}") + + def resourcesDirConfigName = "jsBundleDir${targetName}" + def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?: + file("$buildDir/intermediates/res/merged/${sourceName}") + def jsBundleFile = file("$jsBundleDir/$bundleAssetName") + + // Bundle task name for variant + def bundleJsAndAssetsTaskName = "bundle${targetName}JsAndAssets" + + def currentBundleTask = tasks.create( + name: bundleJsAndAssetsTaskName, + type: Exec) { + group = "react" + description = "bundle JS and assets for ${targetName}." + + // Create dirs if they are not there (e.g. the "clean" task just ran) + doFirst { + jsBundleDir.mkdirs() + resourcesDir.mkdirs() + } + + // Set up inputs and outputs so gradle can cache the result + inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) + outputs.dir jsBundleDir + outputs.dir resourcesDir + + // Set up the call to the react-native cli + workingDir reactRoot + + // Set up dev mode + def devEnabled = !targetName.toLowerCase().contains("release") + if (Os.isFamily(Os.FAMILY_WINDOWS)) { + commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", + "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir + } else { + commandLine "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", + "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir + } + + enabled config."bundleIn${targetName}" ?: targetName.toLowerCase().contains("release") + } + + // Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process + currentBundleTask.dependsOn("merge${targetName}Resources") + currentBundleTask.dependsOn("merge${targetName}Assets") + + runBefore("processArmeabi-v7a${targetName}Resources", currentBundleTask) + runBefore("processX86${targetName}Resources", currentBundleTask) + runBefore("processUniversal${targetName}Resources", currentBundleTask) + runBefore("process${targetName}Resources", currentBundleTask) + } + } +} diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/AndroidManifest.xml b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..0b001a8c --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/java/com/samplern20/MainActivity.java b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/java/com/samplern20/MainActivity.java new file mode 100644 index 00000000..be7fb75f --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/java/com/samplern20/MainActivity.java @@ -0,0 +1,43 @@ +package com.samplern20; + +import com.facebook.react.ReactActivity; +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainReactPackage; + +import java.util.Arrays; +import java.util.List; + +import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage; + +public class MainActivity extends ReactActivity { + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "SampleRN20"; + } + + /** + * Returns whether dev mode should be enabled. + * This enables e.g. the dev menu. + */ + @Override + protected boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + /** + * A list of packages used by the app. If the app uses additional views + * or modules besides the default ones, add more packages here. + */ + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage(), + new WebViewBridgePackage() + ); + } +} diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..cde69bcc Binary files /dev/null and b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..c133a0cb Binary files /dev/null and b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..bfa42f0e Binary files /dev/null and b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..324e72cd Binary files /dev/null and b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/values/strings.xml b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..11d8d6a4 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + SampleRN20 + diff --git a/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/values/styles.xml b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..319eb0ca --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/react-native-webview-bridge/examples/SampleRN20/android/build.gradle b/react-native-webview-bridge/examples/SampleRN20/android/build.gradle new file mode 100644 index 00000000..ccdfc4e3 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/build.gradle @@ -0,0 +1,20 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:1.3.1' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + mavenLocal() + jcenter() + } +} diff --git a/react-native-webview-bridge/examples/SampleRN20/android/gradle.properties b/react-native-webview-bridge/examples/SampleRN20/android/gradle.properties new file mode 100644 index 00000000..1fd964e9 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/gradle.properties @@ -0,0 +1,20 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +android.useDeprecatedNdk=true diff --git a/react-native-webview-bridge/examples/SampleRN20/android/gradle/wrapper/gradle-wrapper.jar b/react-native-webview-bridge/examples/SampleRN20/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..b5166dad Binary files /dev/null and b/react-native-webview-bridge/examples/SampleRN20/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/react-native-webview-bridge/examples/SampleRN20/android/gradle/wrapper/gradle-wrapper.properties b/react-native-webview-bridge/examples/SampleRN20/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..b9fbfaba --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip diff --git a/react-native-webview-bridge/examples/SampleRN20/android/gradlew b/react-native-webview-bridge/examples/SampleRN20/android/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/react-native-webview-bridge/examples/SampleRN20/android/gradlew.bat b/react-native-webview-bridge/examples/SampleRN20/android/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/react-native-webview-bridge/examples/SampleRN20/android/settings.gradle b/react-native-webview-bridge/examples/SampleRN20/android/settings.gradle new file mode 100644 index 00000000..8238010e --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/android/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'SampleRN20' + +include ':app', ':react-native-webview-bridge' +project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') diff --git a/react-native-webview-bridge/examples/SampleRN20/app.js b/react-native-webview-bridge/examples/SampleRN20/app.js new file mode 100644 index 00000000..8e8b6748 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/app.js @@ -0,0 +1,72 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + */ +'use strict'; + +var React = require('react-native'); +var { + StyleSheet, + Text, + View, + WebView +} = React; + +var WebViewBridge = require('react-native-webview-bridge'); + +const injectScript = ` + (function () { + if (WebViewBridge) { + + WebViewBridge.onMessage = function (message) { + if (message === "hello from react-native") { + WebViewBridge.send("got the message inside webview"); + } + }; + + WebViewBridge.send("hello from webview"); + } + }()); +`; + +var Sample2 = React.createClass({ + onBridgeMessage: function (message) { + const { webviewbridge } = this.refs; + + switch (message) { + case "hello from webview": + webviewbridge.sendToBridge("hello from react-native"); + break; + case "got the message inside webview": + console.log("we have got a message from webview! yeah"); + break; + } + }, + render: function() { + return ( + + + + + ); + } +}); + +module.exports = Sample2; + +const styles = StyleSheet.create({ + container: { + flex: 1 + } +}); + diff --git a/react-native-webview-bridge/examples/SampleRN20/index.android.js b/react-native-webview-bridge/examples/SampleRN20/index.android.js new file mode 100644 index 00000000..6e2f7e17 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/index.android.js @@ -0,0 +1,12 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + */ +'use strict'; + +var React = require('react-native'); +var { AppRegistry } = React; + +var Sample2 = require('./app'); + +AppRegistry.registerComponent('SampleRN20', () => Sample2); diff --git a/react-native-webview-bridge/examples/SampleRN20/index.ios.js b/react-native-webview-bridge/examples/SampleRN20/index.ios.js new file mode 100644 index 00000000..6e2f7e17 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/index.ios.js @@ -0,0 +1,12 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + */ +'use strict'; + +var React = require('react-native'); +var { AppRegistry } = React; + +var Sample2 = require('./app'); + +AppRegistry.registerComponent('SampleRN20', () => Sample2); diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20.xcodeproj/project.pbxproj b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20.xcodeproj/project.pbxproj new file mode 100644 index 00000000..c6eb8cb7 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20.xcodeproj/project.pbxproj @@ -0,0 +1,792 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 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 /* SampleRN20Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleRN20Tests.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 */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; + 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 */; }; + 4134948E1C7E0CE200E31AFB /* libReact-Native-Webview-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4134948C1C7E0CDD00E31AFB /* libReact-Native-Webview-Bridge.a */; }; + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTActionSheet; + }; + 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTGeolocation; + }; + 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B511DB1A9E6C8500147676; + remoteInfo = RCTNetwork; + }; + 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTVibration; + }; + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = SampleRN20; + }; + 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; + 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3C86DF461ADF2C930047B81A; + remoteInfo = RCTWebSocket; + }; + 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; + remoteInfo = React; + }; + 4134948B1C7E0CDD00E31AFB /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4134947E1C7E0CDD00E31AFB /* React-Native-Webview-Bridge.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 4114DC4C1C187C3A003CD988; + remoteInfo = "React-Native-Webview-Bridge"; + }; + 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; + 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5119B1A9E6C1200147676; + remoteInfo = RCTText; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; + 00E356EE1AD99517003FC87E /* SampleRN20Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleRN20Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* SampleRN20Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleRN20Tests.m; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* SampleRN20.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleRN20.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = SampleRN20/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = SampleRN20/AppDelegate.m; sourceTree = ""; }; + 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = SampleRN20/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = SampleRN20/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = SampleRN20/main.m; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 4134947E1C7E0CDD00E31AFB /* 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 = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4134948E1C7E0CE200E31AFB /* libReact-Native-Webview-Bridge.a in Frameworks */, + 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00C302A81ABCB8CE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302B61ABCB90400DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302BC1ABCB91800DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302D41ABCB9D200DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302E01ABCB9EE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, + ); + name = Products; + sourceTree = ""; + }; + 00E356EF1AD99517003FC87E /* SampleRN20Tests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* SampleRN20Tests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = SampleRN20Tests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 139105B71AF99BAD00B5F7CC /* Products */ = { + isa = PBXGroup; + children = ( + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, + ); + name = Products; + sourceTree = ""; + }; + 139FDEE71B06529A00C62182 /* Products */ = { + isa = PBXGroup; + children = ( + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, + ); + name = Products; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* SampleRN20 */ = { + isa = PBXGroup; + children = ( + 008F07F21AC5B25A0029DE68 /* main.jsbundle */, + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = SampleRN20; + sourceTree = ""; + }; + 146834001AC3E56700842450 /* Products */ = { + isa = PBXGroup; + children = ( + 146834041AC3E56700842450 /* libReact.a */, + ); + name = Products; + sourceTree = ""; + }; + 4134947F1C7E0CDD00E31AFB /* Products */ = { + isa = PBXGroup; + children = ( + 4134948C1C7E0CDD00E31AFB /* libReact-Native-Webview-Bridge.a */, + ); + name = Products; + sourceTree = ""; + }; + 78C398B11ACF4ADC00677621 /* Products */ = { + isa = PBXGroup; + children = ( + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, + ); + name = Products; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + 4134947E1C7E0CDD00E31AFB /* React-Native-Webview-Bridge.xcodeproj */, + 146833FF1AC3E56700842450 /* React.xcodeproj */, + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, + ); + name = Libraries; + sourceTree = ""; + }; + 832341B11AAA6A8300B99B32 /* Products */ = { + isa = PBXGroup; + children = ( + 832341B51AAA6A8300B99B32 /* libRCTText.a */, + ); + name = Products; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* SampleRN20 */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* SampleRN20Tests */, + 83CBBA001A601CBA00E9B192 /* Products */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* SampleRN20.app */, + 00E356EE1AD99517003FC87E /* SampleRN20Tests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* SampleRN20Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SampleRN20Tests" */; + buildPhases = ( + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = SampleRN20Tests; + productName = SampleRN20Tests; + productReference = 00E356EE1AD99517003FC87E /* SampleRN20Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* SampleRN20 */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SampleRN20" */; + buildPhases = ( + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SampleRN20; + productName = "Hello World"; + productReference = 13B07F961A680F5B00A75B9A /* SampleRN20.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SampleRN20" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; + ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + }, + { + ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; + ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + }, + { + ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; + ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + }, + { + ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; + ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + }, + { + ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; + ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + }, + { + ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; + ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + }, + { + ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; + ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + }, + { + ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; + ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + }, + { + ProductGroup = 139FDEE71B06529A00C62182 /* Products */; + ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + }, + { + ProductGroup = 4134947F1C7E0CDD00E31AFB /* Products */; + ProjectRef = 4134947E1C7E0CDD00E31AFB /* React-Native-Webview-Bridge.xcodeproj */; + }, + { + ProductGroup = 146834001AC3E56700842450 /* Products */; + ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* SampleRN20 */, + 00E356ED1AD99517003FC87E /* SampleRN20Tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTActionSheet.a; + remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTGeolocation.a; + remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTNetwork.a; + remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTVibration.a; + remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 146834041AC3E56700842450 /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 4134948C1C7E0CDD00E31AFB /* libReact-Native-Webview-Bridge.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libReact-Native-Webview-Bridge.a"; + remoteRef = 4134948B1C7E0CDD00E31AFB /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTText.a; + remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* 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 = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* SampleRN20Tests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* SampleRN20 */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 13B07FB21A68108700A75B9A /* Base */, + ); + name = LaunchScreen.xib; + path = SampleRN20; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = SampleRN20Tests/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)/SampleRN20.app/SampleRN20"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(SDKROOT)/Developer/Library/Frameworks", + "$(inherited)", + ); + INFOPLIST_FILE = SampleRN20Tests/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)/SampleRN20.app/SampleRN20"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEAD_CODE_STRIPPING = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + ); + INFOPLIST_FILE = SampleRN20/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = SampleRN20; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + ); + INFOPLIST_FILE = SampleRN20/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = SampleRN20; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 7.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SampleRN20Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SampleRN20" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SampleRN20" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20.xcodeproj/xcshareddata/xcschemes/SampleRN20.xcscheme b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20.xcodeproj/xcshareddata/xcschemes/SampleRN20.xcscheme new file mode 100644 index 00000000..988357e2 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20.xcodeproj/xcshareddata/xcschemes/SampleRN20.xcscheme @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/AppDelegate.h b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/AppDelegate.h new file mode 100644 index 00000000..a9654d5e --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/AppDelegate.h @@ -0,0 +1,16 @@ +/** + * 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. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/AppDelegate.m b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/AppDelegate.m new file mode 100644 index 00000000..7c9c1443 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/AppDelegate.m @@ -0,0 +1,57 @@ +/** + * 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. + */ + +#import "AppDelegate.h" + +#import "RCTRootView.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + NSURL *jsCodeLocation; + + /** + * Loading JavaScript code - uncomment the one you want. + * + * OPTION 1 + * Load from development server. Start the server from the repository root: + * + * $ npm start + * + * To run on device, change `localhost` to the IP address of your computer + * (you can get this by typing `ifconfig` into the terminal and selecting the + * `inet` value under `en0:`) and make sure your computer and iOS device are + * on the same Wi-Fi network. + */ + + jsCodeLocation = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle?platform=ios&dev=true"]; + + /** + * OPTION 2 + * 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:@"SampleRN20" + initialProperties:nil + launchOptions:launchOptions]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Base.lproj/LaunchScreen.xib b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Base.lproj/LaunchScreen.xib new file mode 100644 index 00000000..44c5186c --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Base.lproj/LaunchScreen.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Images.xcassets/AppIcon.appiconset/Contents.json b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..118c98f7 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,38 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Info.plist b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Info.plist new file mode 100644 index 00000000..91963b26 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/Info.plist @@ -0,0 +1,48 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + NSAppTransportSecurity + + + NSAllowsArbitraryLoads + + + + diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/main.m b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/main.m new file mode 100644 index 00000000..3d767fcb --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20/main.m @@ -0,0 +1,18 @@ +/** + * 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. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20Tests/Info.plist b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20Tests/Info.plist new file mode 100644 index 00000000..886825cc --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20Tests/SampleRN20Tests.m b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20Tests/SampleRN20Tests.m new file mode 100644 index 00000000..266d246c --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/ios/SampleRN20Tests/SampleRN20Tests.m @@ -0,0 +1,70 @@ +/** + * 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. + */ + +#import +#import + +#import "RCTLog.h" +#import "RCTRootView.h" + +#define TIMEOUT_SECONDS 240 +#define TEXT_TO_LOOK_FOR @"Welcome to React Native!" + +@interface SampleRN20Tests : XCTestCase + +@end + +@implementation SampleRN20Tests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + + RCTSetLogFunction(RCTDefaultLogFunction); + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + + +@end diff --git a/react-native-webview-bridge/examples/SampleRN20/package.json b/react-native-webview-bridge/examples/SampleRN20/package.json new file mode 100644 index 00000000..b031a531 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/package.json @@ -0,0 +1,12 @@ +{ + "name": "SampleRN20", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "node node_modules/react-native/local-cli/cli.js start" + }, + "dependencies": { + "react-native": "0.20.0", + "react-native-webview-bridge": "../.." + } +} diff --git a/react-native-webview-bridge/examples/SampleRN20/test.html b/react-native-webview-bridge/examples/SampleRN20/test.html new file mode 100644 index 00000000..6dc25e24 --- /dev/null +++ b/react-native-webview-bridge/examples/SampleRN20/test.html @@ -0,0 +1,9 @@ + + + + + + +Hello test html + + \ No newline at end of file diff --git a/react-native-webview-bridge/ios/RCTWebViewBridge.h b/react-native-webview-bridge/ios/RCTWebViewBridge.h new file mode 100644 index 00000000..c7d34222 --- /dev/null +++ b/react-native-webview-bridge/ios/RCTWebViewBridge.h @@ -0,0 +1,48 @@ +/** + * 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 + +@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 + +- (BOOL)webView:(RCTWebViewBridge *)webView +shouldStartLoadForRequest:(NSMutableDictionary *)request + withCallback:(RCTDirectEventBlock)callback; + +@end + +@interface RCTWebViewBridge : RCTView + +@property (nonatomic, weak) id delegate; + +@property (nonatomic, copy) NSDictionary *source; +@property (nonatomic, assign) UIEdgeInsets contentInset; +@property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; +@property (nonatomic, assign) BOOL hideKeyboardAccessoryView; +@property (nonatomic, copy) NSString *injectedJavaScript; + +- (void)goForward; +- (void)goBack; +- (void)reload; +- (void)sendToBridge:(NSString *)message; + +@end diff --git a/react-native-webview-bridge/ios/RCTWebViewBridge.m b/react-native-webview-bridge/ios/RCTWebViewBridge.m new file mode 100644 index 00000000..49984931 --- /dev/null +++ b/react-native-webview-bridge/ios/RCTWebViewBridge.m @@ -0,0 +1,403 @@ +/** + * 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 + +#import +#import +#import +#import +#import +#import +#import "UIView+React.h" +#import + +//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"; + +// runtime trick to remove UIWebview keyboard default toolbar +// see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279 +@interface _SwizzleHelper : NSObject @end +@implementation _SwizzleHelper +-(id)inputAccessoryView +{ + return nil; +} +@end + +@interface RCTWebViewBridge () + +@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)setSource:(NSDictionary *)source +{ + if (![_source isEqualToDictionary:source]) { + _source = [source copy]; + + // Check for a static html source first + NSString *html = [RCTConvert NSString:source[@"html"]]; + if (html) { + NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]]; + [_webView loadHTMLString:html baseURL:baseURL]; + return; + } + + NSURLRequest *request = [RCTConvert NSURLRequest:source]; + // 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 ([request.URL isEqual:_webView.request.URL]) { + return; + } + if (!request.URL) { + // Clear the webview + [_webView loadHTMLString:@"" baseURL:nil]; + return; + } + [_webView loadRequest:request]; + } +} + +- (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 *)baseEvent +{ + NSMutableDictionary *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]; +} + +-(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView +{ + if (!hideKeyboardAccessoryView) { + return; + } + + UIView* subview; + for (UIView* view in _webView.scrollView.subviews) { + if([[view.class description] hasPrefix:@"UIWeb"]) + subview = view; + } + + if(subview == nil) return; + + NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelper", subview.class.superclass]; + Class newClass = NSClassFromString(name); + + if(newClass == nil) + { + newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0); + if(!newClass) return; + + Method method = class_getInstanceMethod([_SwizzleHelper class], @selector(inputAccessoryView)); + class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method)); + + objc_registerClassPair(newClass); + } + + object_setClass(subview, newClass); +} + +#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 *onBridgeMessageEvent = [[NSMutableDictionary alloc] initWithDictionary:@{ + @"messages": [self stringArrayJsonToArray: message] + }]; + + _onBridgeMessage(onBridgeMessageEvent); + + isJSNavigation = YES; + } + + // skip this for the JS Navigation handler + if (!isJSNavigation && _onShouldStartLoadWithRequest) { + NSMutableDictionary *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 *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 *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 *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 = []; + var doc = window.document; + var customEvent = doc.createEvent('Event'); + + 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; + + //dispatch event + customEvent.initEvent('WebViewBridge', true, true); + doc.dispatchEvent(customEvent); + }(window)); + ); +} + +@end diff --git a/react-native-webview-bridge/ios/RCTWebViewBridgeManager.h b/react-native-webview-bridge/ios/RCTWebViewBridgeManager.h new file mode 100644 index 00000000..00578f3f --- /dev/null +++ b/react-native-webview-bridge/ios/RCTWebViewBridgeManager.h @@ -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 + +@interface RCTWebViewBridgeManager : RCTViewManager + +@end diff --git a/react-native-webview-bridge/ios/RCTWebViewBridgeManager.m b/react-native-webview-bridge/ios/RCTWebViewBridgeManager.m new file mode 100644 index 00000000..b7ae548e --- /dev/null +++ b/react-native-webview-bridge/ios/RCTWebViewBridgeManager.m @@ -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 +#import +#import "RCTWebViewBridge.h" +#import "UIView+React.h" + +@interface RCTWebViewBridgeManager () + +@end + +@implementation RCTWebViewBridgeManager +{ + NSConditionLock *_shouldStartLoadLock; + BOOL _shouldStartLoad; +} + +RCT_EXPORT_MODULE() + +- (UIView *)view +{ + RCTWebViewBridge *webView = [RCTWebViewBridge new]; + webView.delegate = self; + return webView; +} + +RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary) +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(hideKeyboardAccessoryView, 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 *)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 *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 *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 *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 *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 *)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 diff --git a/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.pbxproj b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.pbxproj new file mode 100644 index 00000000..8f5454de --- /dev/null +++ b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.pbxproj @@ -0,0 +1,281 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 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 */ + 4114DC4A1C187C3A003CD988 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "include/$(PRODUCT_NAME)"; + dstSubfolderSpec = 16; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 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 = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4114DC491C187C3A003CD988 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4114DC431C187C3A003CD988 = { + isa = PBXGroup; + children = ( + 4114DC4E1C187C3A003CD988 /* React-Native-Webview-Bridge */, + 4114DC5E1C187CE4003CD988 /* scripts */, + 4114DC4D1C187C3A003CD988 /* Products */, + ); + sourceTree = ""; + }; + 4114DC4D1C187C3A003CD988 /* Products */ = { + isa = PBXGroup; + children = ( + 4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */, + ); + name = Products; + sourceTree = ""; + }; + 4114DC4E1C187C3A003CD988 /* React-Native-Webview-Bridge */ = { + isa = PBXGroup; + children = ( + 4114DC581C187CCB003CD988 /* RCTWebViewBridge.h */, + 4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */, + 4114DC5A1C187CCB003CD988 /* RCTWebViewBridgeManager.h */, + 4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */, + ); + path = "React-Native-Webview-Bridge"; + sourceTree = ""; + }; + 4114DC5E1C187CE4003CD988 /* scripts */ = { + isa = PBXGroup; + children = ( + 4114DC5F1C187CE4003CD988 /* webviewbridge.js */, + ); + name = scripts; + path = ../scripts; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4114DC4B1C187C3A003CD988 /* React-Native-Webview-Bridge */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4114DC551C187C3A003CD988 /* Build configuration list for PBXNativeTarget "React-Native-Webview-Bridge" */; + buildPhases = ( + 4114DC481C187C3A003CD988 /* Sources */, + 4114DC491C187C3A003CD988 /* Frameworks */, + 4114DC4A1C187C3A003CD988 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + 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 */ + 4114DC441C187C3A003CD988 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0710; + ORGANIZATIONNAME = alinz; + TargetAttributes = { + 4114DC4B1C187C3A003CD988 = { + CreatedOnToolsVersion = 7.1.1; + }; + }; + }; + buildConfigurationList = 4114DC471C187C3A003CD988 /* Build configuration list for PBXProject "React-Native-Webview-Bridge" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 4114DC431C187C3A003CD988; + productRefGroup = 4114DC4D1C187C3A003CD988 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4114DC4B1C187C3A003CD988 /* React-Native-Webview-Bridge */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4114DC481C187C3A003CD988 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4114DC5D1C187CCB003CD988 /* RCTWebViewBridgeManager.m in Sources */, + 4114DC5C1C187CCB003CD988 /* RCTWebViewBridge.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 4114DC531C187C3A003CD988 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.1; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 4114DC541C187C3A003CD988 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 9.1; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4114DC561C187C3A003CD988 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inhereted)", + "$(SRCROOT)/../../../node_module/React/**", + "$(SRCROOT)/../../../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 4114DC571C187C3A003CD988 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + "$(inhereted)", + "$(SRCROOT)/../../../node_module/React/**", + "$(SRCROOT)/../../../node_modules/react-native/React/**", + ); + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + OTHER_LDFLAGS = "-ObjC"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4114DC471C187C3A003CD988 /* Build configuration list for PBXProject "React-Native-Webview-Bridge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4114DC531C187C3A003CD988 /* Debug */, + 4114DC541C187C3A003CD988 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4114DC551C187C3A003CD988 /* Build configuration list for PBXNativeTarget "React-Native-Webview-Bridge" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4114DC561C187C3A003CD988 /* Debug */, + 4114DC571C187C3A003CD988 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 4114DC441C187C3A003CD988 /* Project object */; +} diff --git a/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..74cbe5b1 --- /dev/null +++ b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..5e877a8b Binary files /dev/null and b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/react-native-webview-bridge.xcscheme b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/react-native-webview-bridge.xcscheme new file mode 100644 index 00000000..e211548a --- /dev/null +++ b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/react-native-webview-bridge.xcscheme @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/xcschememanagement.plist b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..2f988c46 --- /dev/null +++ b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,22 @@ + + + + + SchemeUserState + + React-Native-Webview-Bridge.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 4114DC4B1C187C3A003CD988 + + primary + + + + + diff --git a/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/taxi.xcuserdatad/xcschemes/xcschememanagement.plist b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/taxi.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..35d2065c --- /dev/null +++ b/react-native-webview-bridge/ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/taxi.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + React-Native-Webview-Bridge.xcscheme + + orderHint + 6 + + + + diff --git a/react-native-webview-bridge/package-lock.json b/react-native-webview-bridge/package-lock.json new file mode 100644 index 00000000..738cbf4b --- /dev/null +++ b/react-native-webview-bridge/package-lock.json @@ -0,0 +1,129 @@ +{ + "name": "react-native-webview-bridge", + "version": "0.33.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "core-js": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz", + "integrity": "sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=" + }, + "create-react-class": { + "version": "15.6.2", + "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.6.2.tgz", + "integrity": "sha1-zx7RXxKq1/FO9fLf4F5sQvke8Co=", + "requires": { + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" + } + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "0.4.19" + } + }, + "fbjs": { + "version": "0.8.16", + "resolved": "https://registry.npmjs.org/fbjs/-/fbjs-0.8.16.tgz", + "integrity": "sha1-XmdDL1UNxBtXK/VYR7ispk5TN9s=", + "requires": { + "core-js": "1.2.7", + "isomorphic-fetch": "2.2.1", + "loose-envify": "1.3.1", + "object-assign": "4.1.1", + "promise": "7.3.1", + "setimmediate": "1.0.5", + "ua-parser-js": "0.7.17" + } + }, + "iconv-lite": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", + "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "1.7.3", + "whatwg-fetch": "2.0.3" + } + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "loose-envify": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", + "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", + "requires": { + "js-tokens": "3.0.2" + } + }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "0.1.12", + "is-stream": "1.1.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "promise": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz", + "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==", + "requires": { + "asap": "2.0.6" + } + }, + "prop-types": { + "version": "15.6.0", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.0.tgz", + "integrity": "sha1-zq8IMCL8RrSjX2nhPvda7Q1jmFY=", + "requires": { + "fbjs": "0.8.16", + "loose-envify": "1.3.1", + "object-assign": "4.1.1" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "ua-parser-js": { + "version": "0.7.17", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.17.tgz", + "integrity": "sha512-uRdSdu1oA1rncCQL7sCj8vSyZkgtL7faaw9Tc9rZ3mGgraQ7+Pdx7w5mnOSF3gw9ZNG6oc+KXfkon3bKuROm0g==" + }, + "whatwg-fetch": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz", + "integrity": "sha1-nITsLc9oGH/wC8ZOEnS0QhduHIQ=" + } + } +} diff --git a/react-native-webview-bridge/package.json b/react-native-webview-bridge/package.json new file mode 100644 index 00000000..d95e1726 --- /dev/null +++ b/react-native-webview-bridge/package.json @@ -0,0 +1,38 @@ +{ + "name": "react-native-webview-bridge", + "version": "0.33.0", + "description": "React Native WebView Javascript Bridge", + "main": "webview-bridge", + "directories": { + "example": "example", + "lib": "lib" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git@github.com:alinz/react-native-webview-bridge.git" + }, + "keywords": [ + "react-native", + "react-component", + "ios", + "webview-bridge" + ], + "author": "Ali Najafizadeh", + "contributors": [ + "Hunter Medney" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/alinz/react-native-webview-bridge/issues" + }, + "homepage": "https://github.com/alinz/react-native-webview-bridge", + "dependencies": { + "create-react-class": "^15.6.2", + "invariant": "2.2.0", + "keymirror": "0.1.1", + "prop-types": "^15.6.0" + } +} diff --git a/react-native-webview-bridge/scripts/webviewbridge.js b/react-native-webview-bridge/scripts/webviewbridge.js new file mode 100644 index 00000000..32b43e0f --- /dev/null +++ b/react-native-webview-bridge/scripts/webviewbridge.js @@ -0,0 +1,72 @@ +(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 = []; + var doc = window.document; + var customEvent = doc.createEvent('Event'); + + 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; + + //dispatch event + customEvent.initEvent('WebViewBridge', true, true); + doc.dispatchEvent(customEvent); +}(window)); diff --git a/react-native-webview-bridge/webview-bridge/index.android.js b/react-native-webview-bridge/webview-bridge/index.android.js new file mode 100644 index 00000000..bdfe8180 --- /dev/null +++ b/react-native-webview-bridge/webview-bridge/index.android.js @@ -0,0 +1,233 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * Copyright (c) 2016-present, Ali Najafizadeh + * 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. + * + * @providesModule WebViewBridge + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var invariant = require('invariant'); +var keyMirror = require('keymirror'); +var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); + +var { + ReactNativeViewAttributes, + UIManager, + EdgeInsetsPropType, + StyleSheet, + Text, + View, + WebView, + requireNativeComponent, + DeviceEventEmitter, + NativeModules: { + WebViewBridgeManager + } +} = ReactNative; +var { PropTypes } = React; + +var RCT_WEBVIEWBRIDGE_REF = 'webviewbridge'; + +var WebViewBridgeState = keyMirror({ + IDLE: null, + LOADING: null, + ERROR: null, +}); + +var RCTWebViewBridge = requireNativeComponent('RCTWebViewBridge', WebViewBridge); + +/** + * Renders a native WebView. + */ +var WebViewBridge = React.createClass({ + + propTypes: { + ...RCTWebViewBridge.propTypes, + + /** + * Will be called once the message is being sent from webview + */ + onBridgeMessage: PropTypes.func, + }, + + getInitialState: function() { + return { + viewState: WebViewBridgeState.IDLE, + lastErrorEvent: null, + startInLoadingState: true, + }; + }, + + + componentWillMount: function() { + DeviceEventEmitter.addListener("webViewBridgeMessage", (body) => { + const { onBridgeMessage } = this.props; + const message = body.message; + if (onBridgeMessage) { + onBridgeMessage(message); + } + }); + + if (this.props.startInLoadingState) { + this.setState({viewState: WebViewBridgeState.LOADING}); + } + }, + + render: function() { + var otherView = null; + + if (this.state.viewState === WebViewBridgeState.LOADING) { + otherView = this.props.renderLoading && this.props.renderLoading(); + } else if (this.state.viewState === WebViewBridgeState.ERROR) { + var errorEvent = this.state.lastErrorEvent; + otherView = this.props.renderError && this.props.renderError( + errorEvent.domain, + errorEvent.code, + errorEvent.description); + } else if (this.state.viewState !== WebViewBridgeState.IDLE) { + console.error('RCTWebViewBridge invalid state encountered: ' + this.state.loading); + } + + var webViewStyles = [styles.container, 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 + webViewStyles.push(styles.hidden); + } + + var {javaScriptEnabled, domStorageEnabled} = this.props; + if (this.props.javaScriptEnabledAndroid) { + console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); + javaScriptEnabled = this.props.javaScriptEnabledAndroid; + } + if (this.props.domStorageEnabledAndroid) { + console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); + domStorageEnabled = this.props.domStorageEnabledAndroid; + } + + let {source, ...props} = {...this.props}; + + var webView = + ; + + return ( + + {webView} + {otherView} + + ); + }, + + onMessage(event) { + if (this.props.onBridgeMessage != null && event.nativeEvent != null) { + this.props.onBridgeMessage(event.nativeEvent.message) + } + }, + + goForward: function() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goForward, + null + ); + }, + + goBack: function() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goBack, + null + ); + }, + + reload: function() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.reload, + null + ); + }, + + sendToBridge: function (message: string) { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.sendToBridge, + [message] + ); + }, + + /** + * We return an event with a bunch of fields including: + * url, title, loading, canGoBack, canGoForward + */ + updateNavigationState: function(event) { + if (this.props.onNavigationStateChange) { + this.props.onNavigationStateChange(event.nativeEvent); + } + }, + + getWebViewBridgeHandle: function() { + return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEWBRIDGE_REF]); + }, + + onLoadingStart: function(event) { + var onLoadStart = this.props.onLoadStart; + onLoadStart && onLoadStart(event); + this.updateNavigationState(event); + }, + + onLoadingError: function(event) { + event.persist(); // persist this event because we need to store it + var {onError, onLoadEnd} = this.props; + onError && onError(event); + onLoadEnd && onLoadEnd(event); + + this.setState({ + lastErrorEvent: event.nativeEvent, + viewState: WebViewBridgeState.ERROR + }); + }, + + onLoadingFinish: function(event) { + var {onLoad, onLoadEnd} = this.props; + onLoad && onLoad(event); + onLoadEnd && onLoadEnd(event); + this.setState({ + viewState: WebViewBridgeState.IDLE, + }); + this.updateNavigationState(event); + }, +}); + + +var styles = StyleSheet.create({ + container: { + flex: 1, + }, + hidden: { + height: 0, + flex: 0, // disable 'flex:1' when hiding a View + }, +}); + +module.exports = WebViewBridge; \ No newline at end of file diff --git a/react-native-webview-bridge/webview-bridge/index.ios.js b/react-native-webview-bridge/webview-bridge/index.ios.js new file mode 100644 index 00000000..f7b09e5e --- /dev/null +++ b/react-native-webview-bridge/webview-bridge/index.ios.js @@ -0,0 +1,324 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * Copyright (c) 2016-present, Ali Najafizadeh + * 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. + * + * @providesModule WebViewBridge + * @flow + */ +'use strict'; + +var React = require('react'); +var ReactNative = require('react-native'); +var invariant = require('invariant'); +var keyMirror = require('keymirror'); +var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); +var PropTypes = require('prop-types'); + +var { + ActivityIndicator, + EdgeInsetsPropType, + StyleSheet, + Text, + View, + WebView, + requireNativeComponent, + UIManager, + NativeModules: { + WebViewBridgeManager + } +} = ReactNative; + + +var BGWASH = 'rgba(255,255,255,0.8)'; +var RCT_WEBVIEWBRIDGE_REF = 'webviewbridge'; + +var RCTWebViewBridgeManager = WebViewBridgeManager; + +var WebViewBridgeState = keyMirror({ + IDLE: null, + LOADING: null, + ERROR: null, +}); + +var NavigationType = { + click: RCTWebViewBridgeManager.NavigationType.LinkClicked, + formsubmit: RCTWebViewBridgeManager.NavigationType.FormSubmitted, + backforward: RCTWebViewBridgeManager.NavigationType.BackForward, + reload: RCTWebViewBridgeManager.NavigationType.Reload, + formresubmit: RCTWebViewBridgeManager.NavigationType.FormResubmitted, + other: RCTWebViewBridgeManager.NavigationType.Other, +}; + +var JSNavigationScheme = RCTWebViewBridgeManager.JSNavigationScheme; + +type ErrorEvent = { + domain: any; + code: any; + description: any; +} + +type Event = Object; + +var defaultRenderLoading = () => ( + + + +); +var defaultRenderError = (errorDomain, errorCode, errorDesc) => ( + + + Error loading page + + + {'Domain: ' + errorDomain} + + + {'Error Code: ' + errorCode} + + + {'Description: ' + errorDesc} + + +); + +/** + * Renders a native WebView. + */ +var createReactClass = require('create-react-class'); +var WebViewBridge = createReactClass({ + statics: { + JSNavigationScheme: JSNavigationScheme, + NavigationType: NavigationType, + }, + + propTypes: { + ...WebView.propTypes, + + /** + * Will be called once the message is being sent from webview + */ + onBridgeMessage: PropTypes.func, + + hideKeyboardAccessoryView: PropTypes.bool, + + keyboardDisplayRequiresUserAction: PropTypes.bool, + }, + + 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 webViewStyles = [styles.container, styles.webView, 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 + webViewStyles.push(styles.hidden); + } + + var onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => { + var shouldStart = this.props.onShouldStartLoadWithRequest && + this.props.onShouldStartLoadWithRequest(event.nativeEvent); + RCTWebViewBridgeManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier); + }); + + var {javaScriptEnabled, domStorageEnabled} = this.props; + if (this.props.javaScriptEnabledAndroid) { + console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); + javaScriptEnabled = this.props.javaScriptEnabledAndroid; + } + if (this.props.domStorageEnabledAndroid) { + console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); + domStorageEnabled = this.props.domStorageEnabledAndroid; + } + + var onBridgeMessage = (event: Event) => { + const onBridgeMessageCallback = this.props.onBridgeMessage; + if (onBridgeMessageCallback) { + const messages = event.nativeEvent.messages; + messages.forEach((message) => { + onBridgeMessageCallback(message); + }); + } + }; + + let {source, ...props} = {...this.props}; + delete props.onBridgeMessage; + delete props.onShouldStartLoadWithRequest; + + var webView = + ; + + return ( + + {webView} + {otherView} + + ); + }, + + goForward: function() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goForward, + null + ); + }, + + goBack: function() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.goBack, + null + ); + }, + + reload: function() { + UIManager.dispatchViewManagerCommand( + this.getWebViewBridgeHandle(), + UIManager.RCTWebViewBridge.Commands.reload, + null + ); + }, + + sendToBridge: function (message: string) { + 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 ReactNative.findNodeHandle(this.refs[RCT_WEBVIEWBRIDGE_REF]); + }, + + onLoadingStart: function(event: Event) { + var onLoadStart = this.props.onLoadStart; + onLoadStart && onLoadStart(event); + this.updateNavigationState(event); + }, + + onLoadingError: function(event: Event) { + event.persist(); // persist this event because we need to store it + var {onError, onLoadEnd} = this.props; + onError && onError(event); + onLoadEnd && onLoadEnd(event); + console.warn('Encountered an error loading page', event.nativeEvent); + + this.setState({ + lastErrorEvent: event.nativeEvent, + viewState: WebViewBridgeState.ERROR + }); + }, + + onLoadingFinish: function(event: Event) { + var {onLoad, onLoadEnd} = this.props; + onLoad && onLoad(event); + onLoadEnd && onLoadEnd(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', + }, + webView: { + backgroundColor: '#ffffff', + } +}); + +module.exports = WebViewBridge;