├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── github │ └── alinz │ └── reactnativewebviewbridge │ ├── JavascriptBridge.java │ ├── WebViewBridgeManager.java │ └── WebViewBridgePackage.java ├── doc └── assets │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ └── 05.png ├── example ├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ ├── react.gradle │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── samplern20 │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── app.js ├── app.json ├── index.android.js ├── index.ios.js ├── ios │ ├── SampleRN20.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── SampleRN20-tvOS.xcscheme │ │ │ └── SampleRN20.xcscheme │ ├── SampleRN20 │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── main.m │ └── SampleRN20Tests │ │ ├── Info.plist │ │ └── SampleRN20Tests.m ├── package.json ├── test.html └── yarn.lock ├── ios ├── RCTWebViewBridge.h ├── RCTWebViewBridge.m ├── RCTWebViewBridgeManager.h ├── RCTWebViewBridgeManager.m └── React-Native-Webview-Bridge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── ali.xcuserdatad │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ └── ali.xcuserdatad │ └── xcschemes │ ├── react-native-webview-bridge.xcscheme │ └── xcschememanagement.plist ├── package.json ├── scripts └── webviewbridge.js └── webview-bridge ├── index.android.js └── index.ios.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ############ 3 | # Node 4 | ############ 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | 10 | # Runtime data 11 | pids 12 | *.pid 13 | *.seed 14 | 15 | # Directory for instrumented libs generated by jscoverage/JSCover 16 | lib-cov 17 | 18 | # Coverage directory used by tools like istanbul 19 | coverage 20 | 21 | # nyc test coverage 22 | .nyc_output 23 | 24 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 25 | .grunt 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # Compiled binary addons (http://nodejs.org/api/addons.html) 31 | build/Release 32 | 33 | # Dependency directories 34 | node_modules 35 | jspm_packages 36 | 37 | # Optional npm cache directory 38 | .npm 39 | 40 | # Optional REPL history 41 | .node_repl_history 42 | 43 | ################ 44 | # JetBrains 45 | ################ 46 | .idea 47 | 48 | ## File-based project format: 49 | *.iws 50 | 51 | ## Plugin-specific files: 52 | 53 | # IntelliJ 54 | /out/ 55 | 56 | # mpeltonen/sbt-idea plugin 57 | .idea_modules/ 58 | 59 | # JIRA plugin 60 | atlassian-ide-plugin.xml 61 | 62 | # Crashlytics plugin (for Android Studio and IntelliJ) 63 | com_crashlytics_export_strings.xml 64 | crashlytics.properties 65 | crashlytics-build.properties 66 | fabric.properties 67 | 68 | 69 | ############ 70 | # iOS 71 | ############ 72 | # Xcode 73 | # 74 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 75 | 76 | ## Build generated 77 | ios/build/ 78 | ios/DerivedData/ 79 | 80 | ## Various settings 81 | *.pbxuser 82 | !default.pbxuser 83 | *.mode1v3 84 | !default.mode1v3 85 | *.mode2v3 86 | !default.mode2v3 87 | *.perspectivev3 88 | !default.perspectivev3 89 | ios/xcuserdata/ 90 | 91 | ## Other 92 | *.moved-aside 93 | *.xcuserstate 94 | 95 | ## Obj-C/Swift specific 96 | *.hmap 97 | *.ipa 98 | *.dSYM.zip 99 | *.dSYM 100 | 101 | # CocoaPods 102 | # 103 | # We recommend against adding the Pods directory to your .gitignore. However 104 | # you should judge for yourself, the pros and cons are mentioned at: 105 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 106 | # 107 | ios/Pods/ 108 | 109 | # Carthage 110 | # 111 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 112 | # Carthage/Checkouts 113 | 114 | Carthage/Build 115 | 116 | # fastlane 117 | # 118 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 119 | # screenshots whenever they are needed. 120 | # For more information about the recommended setup visit: 121 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 122 | 123 | fastlane/report.xml 124 | fastlane/screenshots 125 | 126 | 127 | ############ 128 | # Android 129 | ############ 130 | # Built application files 131 | *.apk 132 | *.ap_ 133 | 134 | # Files for the Dalvik VM 135 | *.dex 136 | 137 | # Java class files 138 | *.class 139 | 140 | # Generated files 141 | android/bin/ 142 | android/gen/ 143 | android/out/ 144 | 145 | # Gradle files 146 | android/.gradle/ 147 | android/build/ 148 | 149 | # Local configuration file (sdk path, etc) 150 | local.properties 151 | 152 | # Proguard folder generated by Eclipse 153 | android/proguard/ 154 | 155 | # Log Files 156 | *.log 157 | 158 | # Android Studio Navigation editor temp files 159 | android/.navigation/ 160 | 161 | # Android Studio captures folder 162 | android/captures/ 163 | 164 | # Intellij 165 | *.iml 166 | 167 | # Keystore files 168 | *.jks 169 | 170 | ################## 171 | # React-Native 172 | ################## 173 | # OSX 174 | # 175 | .DS_Store 176 | 177 | # Xcode 178 | # 179 | build/ 180 | *.pbxuser 181 | !default.pbxuser 182 | *.mode1v3 183 | !default.mode1v3 184 | *.mode2v3 185 | !default.mode2v3 186 | *.perspectivev3 187 | !default.perspectivev3 188 | xcuserdata 189 | *.xccheckout 190 | *.moved-aside 191 | DerivedData 192 | *.hmap 193 | *.ipa 194 | *.xcuserstate 195 | project.xcworkspace 196 | 197 | # Android/IJ 198 | # 199 | .idea 200 | .gradle 201 | local.properties 202 | 203 | # node.js 204 | # 205 | node_modules/ 206 | npm-debug.log 207 | 208 | # BUCK 209 | buck-out/ 210 | \.buckd/ 211 | android/app/libs 212 | android/keystores/debug.keystore 213 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test/ 3 | examples/ 4 | doc/ 5 | 6 | .npmignore 7 | 8 | ################# 9 | # from .gitignore: 10 | ################ 11 | 12 | 13 | ############ 14 | # Node 15 | ############ 16 | # Logs 17 | logs 18 | *.log 19 | npm-debug.log* 20 | 21 | # Runtime data 22 | pids 23 | *.pid 24 | *.seed 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | 32 | # nyc test coverage 33 | .nyc_output 34 | 35 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 36 | .grunt 37 | 38 | # node-waf configuration 39 | .lock-wscript 40 | 41 | # Compiled binary addons (http://nodejs.org/api/addons.html) 42 | build/Release 43 | 44 | # Dependency directories 45 | node_modules 46 | jspm_packages 47 | 48 | # Optional npm cache directory 49 | .npm 50 | 51 | # Optional REPL history 52 | .node_repl_history 53 | 54 | ################ 55 | # JetBrains 56 | ################ 57 | .idea 58 | 59 | ## File-based project format: 60 | *.iws 61 | 62 | ## Plugin-specific files: 63 | 64 | # IntelliJ 65 | /out/ 66 | 67 | # mpeltonen/sbt-idea plugin 68 | .idea_modules/ 69 | 70 | # JIRA plugin 71 | atlassian-ide-plugin.xml 72 | 73 | # Crashlytics plugin (for Android Studio and IntelliJ) 74 | com_crashlytics_export_strings.xml 75 | crashlytics.properties 76 | crashlytics-build.properties 77 | fabric.properties 78 | 79 | 80 | ############ 81 | # iOS 82 | ############ 83 | # Xcode 84 | # 85 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 86 | 87 | ## Build generated 88 | ios/build/ 89 | ios/DerivedData/ 90 | 91 | ## Various settings 92 | *.pbxuser 93 | !default.pbxuser 94 | *.mode1v3 95 | !default.mode1v3 96 | *.mode2v3 97 | !default.mode2v3 98 | *.perspectivev3 99 | !default.perspectivev3 100 | ios/xcuserdata/ 101 | 102 | ## Other 103 | *.moved-aside 104 | *.xcuserstate 105 | 106 | ## Obj-C/Swift specific 107 | *.hmap 108 | *.ipa 109 | *.dSYM.zip 110 | *.dSYM 111 | 112 | # CocoaPods 113 | # 114 | # We recommend against adding the Pods directory to your .gitignore. However 115 | # you should judge for yourself, the pros and cons are mentioned at: 116 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 117 | # 118 | ios/Pods/ 119 | 120 | # Carthage 121 | # 122 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 123 | # Carthage/Checkouts 124 | 125 | Carthage/Build 126 | 127 | # fastlane 128 | # 129 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 130 | # screenshots whenever they are needed. 131 | # For more information about the recommended setup visit: 132 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 133 | 134 | fastlane/report.xml 135 | fastlane/screenshots 136 | 137 | 138 | ############ 139 | # Android 140 | ############ 141 | # Built application files 142 | *.apk 143 | *.ap_ 144 | 145 | # Files for the Dalvik VM 146 | *.dex 147 | 148 | # Java class files 149 | *.class 150 | 151 | # Generated files 152 | android/bin/ 153 | android/gen/ 154 | android/out/ 155 | 156 | # Gradle files 157 | .gradle/ 158 | android/.gradle/ 159 | android/build/ 160 | android/*/build/ 161 | 162 | # Local configuration file (sdk path, etc) 163 | local.properties 164 | 165 | # Proguard folder generated by Eclipse 166 | android/proguard/ 167 | 168 | # Log Files 169 | *.log 170 | 171 | # Android Studio Navigation editor temp files 172 | android/.navigation/ 173 | 174 | # Android Studio captures folder 175 | android/captures/ 176 | 177 | # Intellij 178 | *.iml 179 | 180 | # Keystore files 181 | *.jks 182 | 183 | ################## 184 | # React-Native 185 | ################## 186 | # OSX 187 | # 188 | .DS_Store 189 | 190 | # Xcode 191 | # 192 | build/ 193 | *.pbxuser 194 | !default.pbxuser 195 | *.mode1v3 196 | !default.mode1v3 197 | *.mode2v3 198 | !default.mode2v3 199 | *.perspectivev3 200 | !default.perspectivev3 201 | xcuserdata 202 | *.xccheckout 203 | *.moved-aside 204 | DerivedData 205 | *.hmap 206 | *.ipa 207 | *.xcuserstate 208 | project.xcworkspace 209 | 210 | # Android/IJ 211 | # 212 | .idea 213 | android/.idea 214 | android/.gradle 215 | android/local.properties 216 | 217 | # node.js 218 | # 219 | node_modules/ 220 | npm-debug.log 221 | 222 | # BUCK 223 | buck-out/ 224 | \.buckd/ 225 | android/app/libs 226 | android/keystores/debug.keystore 227 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ali Najafizadeh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Please take a look at this [issue](https://github.com/alinz/react-native-webview-bridge/issues/109) first 2 | 3 | # React Native WebView Javascript Bridge 4 | 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. 5 | 6 | 7 | 8 | ## Installation 9 | 10 | In order to use this extension, you have to do the following steps: 11 | 12 | in your react-native project, run `npm install react-native-webview-bridge --save` 13 | 14 | ### iOS 15 | 16 | 1. go to xcode's `Project Navigator` tab 17 |

18 | 19 |

20 | 2. right click on `Libraries` 21 | 3. select `Add Files to ...` option 22 |

23 | 24 |

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

27 | 28 |

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

31 | 32 |

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

36 | 37 |

38 | 8. clean compile to make sure your project can compile and build. 39 | 40 | ### Android 41 | 42 | 1. add the following import to `MainApplication.java` (`MainActivity.java` if RN < 0.29) of your application 43 | 44 | ```java 45 | import com.github.alinz.reactnativewebviewbridge.WebViewBridgePackage; 46 | ``` 47 | 48 | 2. add the following code to add the package to `MainApplication.java`` (`MainActivity.java` if RN < 0.29) 49 | 50 | ```java 51 | protected List getPackages() { 52 | return Arrays.asList( 53 | new MainReactPackage(), 54 | new WebViewBridgePackage() //<- this 55 | ); 56 | } 57 | ``` 58 | 59 | 3. add the following codes to your `android/setting.gradle` 60 | 61 | > you might have multiple 3rd party libraries, make sure that you don't create multiple include. 62 | 63 | ``` 64 | include ':app', ':react-native-webview-bridge' 65 | project(':react-native-webview-bridge').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview-bridge/android') 66 | ``` 67 | 68 | 4. edit `android/app/build.gradle` and add the following line inside `dependencies` 69 | 70 | ``` 71 | compile project(':react-native-webview-bridge') 72 | ``` 73 | 74 | 5. run `react-native run-android` to see if everything is compilable. 75 | 76 | ## Usage 77 | 78 | just import the module with one of your choices way: 79 | 80 | ** CommonJS style ** 81 | 82 | ```js 83 | var WebViewBridge = require('react-native-webview-bridge'); 84 | ``` 85 | 86 | ** ES6/ES2015 style ** 87 | 88 | ```js 89 | import WebViewBridge from 'react-native-webview-bridge'; 90 | ``` 91 | 92 | `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. 93 | 94 | #### sendToBridge(message) 95 | the message must be in string. because this is the only way to send data back and forth between native and webview. 96 | 97 | 98 | #### onBridgeMessage 99 | 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. 100 | 101 | #### allowFileAccessFromFileURLs (Android only) 102 | 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. 103 | 104 | #### allowUniversalAccessFromFileURLs (Android only) 105 | 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. 106 | 107 | ## Bridge Script 108 | 109 | 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. 110 | 111 | #### send(message) 112 | 113 | this method sends a message to native side. the message must be in string type or `onError` method will be called. 114 | 115 | #### onMessage 116 | 117 | this method needs to be implemented. it will be called once a message arrives from native side. The type of message is in string. 118 | 119 | #### onError (iOS only) 120 | 121 | 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. 122 | 123 | ## Notes 124 | 125 | > 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. 126 | 127 | > You can still pass your own javascript to be injected into webview. However, Bridge script will be injected first and then your custom script. 128 | 129 | 130 | ## Simple Example 131 | This example can be found in `examples` folder. 132 | 133 | ```js 134 | const injectScript = ` 135 | (function () { 136 | if (WebViewBridge) { 137 | 138 | WebViewBridge.onMessage = function (message) { 139 | if (message === "hello from react-native") { 140 | WebViewBridge.send("got the message inside webview"); 141 | } 142 | }; 143 | 144 | WebViewBridge.send("hello from webview"); 145 | } 146 | }()); 147 | `; 148 | 149 | var Sample2 = React.createClass({ 150 | onBridgeMessage(message){ 151 | const { webviewbridge } = this.refs; 152 | 153 | switch (message) { 154 | case "hello from webview": 155 | webviewbridge.sendToBridge("hello from react-native"); 156 | break; 157 | case "got the message inside webview": 158 | console.log("we have got a message from webview! yeah"); 159 | break; 160 | } 161 | }, 162 | 163 | render() { 164 | return ( 165 | 170 | ); 171 | } 172 | }); 173 | ``` 174 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | 6 | dependencies { 7 | classpath 'com.android.tools.build:gradle:1.1.3' 8 | } 9 | } 10 | 11 | apply plugin: 'com.android.library' 12 | 13 | android { 14 | compileSdkVersion 23 15 | buildToolsVersion "23.0.1" 16 | 17 | defaultConfig { 18 | minSdkVersion 16 19 | targetSdkVersion 23 20 | versionCode 1 21 | versionName "1.0" 22 | } 23 | lintOptions { 24 | abortOnError false 25 | } 26 | } 27 | 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | compile 'com.facebook.react:react-native:0.19.+' 34 | } 35 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/alinz/reactnativewebviewbridge/JavascriptBridge.java: -------------------------------------------------------------------------------- 1 | package com.github.alinz.reactnativewebviewbridge; 2 | 3 | import android.webkit.JavascriptInterface; 4 | import android.webkit.WebView; 5 | 6 | import com.facebook.react.bridge.Arguments; 7 | import com.facebook.react.bridge.ReactContext; 8 | import com.facebook.react.bridge.WritableMap; 9 | import com.facebook.react.modules.core.DeviceEventManagerModule; 10 | import com.facebook.react.uimanager.events.RCTEventEmitter; 11 | 12 | class JavascriptBridge { 13 | private WebView webView; 14 | 15 | public JavascriptBridge(WebView webView) { 16 | this.webView = webView; 17 | } 18 | 19 | @JavascriptInterface 20 | public void send(String message) { 21 | WritableMap event = Arguments.createMap(); 22 | event.putString("message", message); 23 | ReactContext reactContext = (ReactContext) this.webView.getContext(); 24 | reactContext.getJSModule(RCTEventEmitter.class).receiveEvent( 25 | this.webView.getId(), 26 | "topChange", 27 | event); 28 | } 29 | } -------------------------------------------------------------------------------- /android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgeManager.java: -------------------------------------------------------------------------------- 1 | package com.github.alinz.reactnativewebviewbridge; 2 | 3 | import android.webkit.WebView; 4 | 5 | import com.facebook.react.bridge.ReadableArray; 6 | import com.facebook.react.uimanager.ThemedReactContext; 7 | import com.facebook.react.views.webview.ReactWebViewManager; 8 | import com.facebook.react.uimanager.annotations.ReactProp; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Map; 12 | 13 | import javax.annotation.Nullable; 14 | 15 | public class WebViewBridgeManager extends ReactWebViewManager { 16 | private static final String REACT_CLASS = "RCTWebViewBridge"; 17 | 18 | public static final int COMMAND_SEND_TO_BRIDGE = 101; 19 | 20 | @Override 21 | public String getName() { 22 | return REACT_CLASS; 23 | } 24 | 25 | @Override 26 | public 27 | @Nullable 28 | Map getCommandsMap() { 29 | Map commandsMap = super.getCommandsMap(); 30 | 31 | commandsMap.put("sendToBridge", COMMAND_SEND_TO_BRIDGE); 32 | 33 | return commandsMap; 34 | } 35 | 36 | @Override 37 | protected WebView createViewInstance(ThemedReactContext reactContext) { 38 | WebView root = super.createViewInstance(reactContext); 39 | root.addJavascriptInterface(new JavascriptBridge(root), "WebViewBridge"); 40 | return root; 41 | } 42 | 43 | @Override 44 | public void receiveCommand(WebView root, int commandId, @Nullable ReadableArray args) { 45 | super.receiveCommand(root, commandId, args); 46 | 47 | switch (commandId) { 48 | case COMMAND_SEND_TO_BRIDGE: 49 | sendToBridge(root, args.getString(0)); 50 | break; 51 | default: 52 | //do nothing!!!! 53 | } 54 | } 55 | 56 | private void sendToBridge(WebView root, String message) { 57 | String script = "WebViewBridge.onMessage('" + message + "');"; 58 | WebViewBridgeManager.evaluateJavascript(root, script); 59 | } 60 | 61 | static private void evaluateJavascript(WebView root, String javascript) { 62 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) { 63 | root.evaluateJavascript(javascript, null); 64 | } else { 65 | root.loadUrl("javascript:" + javascript); 66 | } 67 | } 68 | 69 | @ReactProp(name = "allowFileAccessFromFileURLs") 70 | public void setAllowFileAccessFromFileURLs(WebView root, boolean allows) { 71 | root.getSettings().setAllowFileAccessFromFileURLs(allows); 72 | } 73 | 74 | @ReactProp(name = "allowUniversalAccessFromFileURLs") 75 | public void setAllowUniversalAccessFromFileURLs(WebView root, boolean allows) { 76 | root.getSettings().setAllowUniversalAccessFromFileURLs(allows); 77 | } 78 | } -------------------------------------------------------------------------------- /android/src/main/java/com/github/alinz/reactnativewebviewbridge/WebViewBridgePackage.java: -------------------------------------------------------------------------------- 1 | package com.github.alinz.reactnativewebviewbridge; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | public class WebViewBridgePackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactApplicationContext) { 16 | return new ArrayList<>(); 17 | } 18 | 19 | @Override 20 | public List createViewManagers(ReactApplicationContext reactApplicationContext) { 21 | return Arrays.asList( 22 | new WebViewBridgeManager() 23 | ); 24 | } 25 | 26 | @Override 27 | public List> createJSModules() { 28 | return Arrays.asList(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /doc/assets/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/doc/assets/01.png -------------------------------------------------------------------------------- /doc/assets/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/doc/assets/02.png -------------------------------------------------------------------------------- /doc/assets/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/doc/assets/03.png -------------------------------------------------------------------------------- /doc/assets/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/doc/assets/04.png -------------------------------------------------------------------------------- /doc/assets/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/doc/assets/05.png -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | 3 | # We fork some components by platform. 4 | .*/*.web.js 5 | .*/*.android.js 6 | 7 | # Some modules have their own node_modules with overlap 8 | .*/node_modules/node-haste/.* 9 | 10 | # Ugh 11 | .*/node_modules/babel.* 12 | .*/node_modules/babylon.* 13 | .*/node_modules/invariant.* 14 | 15 | # Ignore react and fbjs where there are overlaps, but don't ignore 16 | # anything that react-native relies on 17 | .*/node_modules/fbjs/lib/Map.js 18 | .*/node_modules/fbjs/lib/Promise.js 19 | .*/node_modules/fbjs/lib/fetch.js 20 | .*/node_modules/fbjs/lib/ExecutionEnvironment.js 21 | .*/node_modules/fbjs/lib/isEmpty.js 22 | .*/node_modules/fbjs/lib/crc32.js 23 | .*/node_modules/fbjs/lib/ErrorUtils.js 24 | 25 | # Flow has a built-in definition for the 'react' module which we prefer to use 26 | # over the currently-untyped source 27 | .*/node_modules/react/react.js 28 | .*/node_modules/react/lib/React.js 29 | .*/node_modules/react/lib/ReactDOM.js 30 | 31 | # Ignore commoner tests 32 | .*/node_modules/commoner/test/.* 33 | 34 | # See https://github.com/facebook/flow/issues/442 35 | .*/react-tools/node_modules/commoner/lib/reader.js 36 | 37 | # Ignore jest 38 | .*/node_modules/jest-cli/.* 39 | 40 | # Ignore Website 41 | .*/website/.* 42 | 43 | [include] 44 | 45 | [libs] 46 | node_modules/react-native/Libraries/react-native/react-native-interface.js 47 | 48 | [options] 49 | module.system=haste 50 | 51 | munge_underscores=true 52 | 53 | module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub' 54 | 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' 55 | 56 | suppress_type=$FlowIssue 57 | suppress_type=$FlowFixMe 58 | suppress_type=$FixMe 59 | 60 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 61 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(2[0-1]\\|1[0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 62 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 63 | 64 | [version] 65 | 0.21.0 66 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IJ 26 | # 27 | .idea 28 | .gradle 29 | local.properties 30 | 31 | # node.js 32 | # 33 | node_modules/ 34 | npm-debug.log 35 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.samplern20", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.samplern20", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // the root of your project, i.e. where "package.json" lives 37 | * root: "../../", 38 | * 39 | * // where to put the JS bundle asset in debug mode 40 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 41 | * 42 | * // where to put the JS bundle asset in release mode 43 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 44 | * 45 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 46 | * // require('./image.png')), in debug mode 47 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 48 | * 49 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 50 | * // require('./image.png')), in release mode 51 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 52 | * 53 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 54 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 55 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 56 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 57 | * // for example, you might want to remove it from here. 58 | * inputExcludes: ["android/**", "ios/**"], 59 | * 60 | * // override which node gets called and with what additional arguments 61 | * nodeExecutableAndArgs: ["node"] 62 | * 63 | * // supply additional arguments to the packager 64 | * extraPackagerArgs: [] 65 | * ] 66 | */ 67 | 68 | apply from: "../../node_modules/react-native/react.gradle" 69 | 70 | /** 71 | * Set this to true to create two separate APKs instead of one: 72 | * - An APK that only works on ARM devices 73 | * - An APK that only works on x86 devices 74 | * The advantage is the size of the APK is reduced by about 4MB. 75 | * Upload all the APKs to the Play Store and people will download 76 | * the correct one based on the CPU architecture of their device. 77 | */ 78 | def enableSeparateBuildPerCPUArchitecture = false 79 | 80 | /** 81 | * Run Proguard to shrink the Java bytecode in release builds. 82 | */ 83 | def enableProguardInReleaseBuilds = false 84 | 85 | android { 86 | compileSdkVersion 23 87 | buildToolsVersion "23.0.1" 88 | 89 | defaultConfig { 90 | applicationId "com.samplern20" 91 | minSdkVersion 16 92 | targetSdkVersion 22 93 | versionCode 1 94 | versionName "1.0" 95 | ndk { 96 | abiFilters "armeabi-v7a", "x86" 97 | } 98 | } 99 | splits { 100 | abi { 101 | reset() 102 | enable enableSeparateBuildPerCPUArchitecture 103 | universalApk false // If true, also generate a universal APK 104 | include "armeabi-v7a", "x86" 105 | } 106 | } 107 | buildTypes { 108 | release { 109 | minifyEnabled enableProguardInReleaseBuilds 110 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 111 | } 112 | } 113 | // applicationVariants are e.g. debug, release 114 | applicationVariants.all { variant -> 115 | variant.outputs.each { output -> 116 | // For each separate APK per architecture, set a unique version code as described here: 117 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 118 | def versionCodes = ["armeabi-v7a":1, "x86":2] 119 | def abi = output.getFilter(OutputFile.ABI) 120 | if (abi != null) { // null for the universal-debug, universal-release variants 121 | output.versionCodeOverride = 122 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 123 | } 124 | } 125 | } 126 | } 127 | 128 | dependencies { 129 | compile fileTree(dir: "libs", include: ["*.jar"]) 130 | compile "com.android.support:appcompat-v7:23.0.1" 131 | compile "com.facebook.react:react-native:+" // From node_modules 132 | } 133 | 134 | // Run this once to be able to run the application with BUCK 135 | // puts all compile dependencies into folder libs for BUCK to use 136 | task copyDownloadableDepsToLibs(type: Copy) { 137 | from configurations.compile 138 | into 'libs' 139 | } 140 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # okhttp 54 | 55 | -keepattributes Signature 56 | -keepattributes *Annotation* 57 | -keep class okhttp3.** { *; } 58 | -keep interface okhttp3.** { *; } 59 | -dontwarn okhttp3.** 60 | 61 | # okio 62 | 63 | -keep class sun.misc.Unsafe { *; } 64 | -dontwarn java.nio.file.* 65 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 66 | -dontwarn okio.** 67 | -------------------------------------------------------------------------------- /example/android/app/react.gradle: -------------------------------------------------------------------------------- 1 | import org.apache.tools.ant.taskdefs.condition.Os 2 | 3 | def config = project.hasProperty("react") ? project.react : []; 4 | 5 | def bundleAssetName = config.bundleAssetName ?: "index.android.bundle" 6 | def entryFile = config.entryFile ?: "index.android.js" 7 | 8 | // because elvis operator 9 | def elvisFile(thing) { 10 | return thing ? file(thing) : null; 11 | } 12 | 13 | def reactRoot = elvisFile(config.root) ?: file("../../") 14 | def inputExcludes = config.inputExcludes ?: ["android/**", "ios/**"] 15 | 16 | void runBefore(String dependentTaskName, Task task) { 17 | Task dependentTask = tasks.findByPath(dependentTaskName); 18 | if (dependentTask != null) { 19 | dependentTask.dependsOn task 20 | } 21 | } 22 | 23 | gradle.projectsEvaluated { 24 | // Grab all build types and product flavors 25 | def buildTypes = android.buildTypes.collect { type -> type.name } 26 | def productFlavors = android.productFlavors.collect { flavor -> flavor.name } 27 | 28 | // When no product flavors defined, use empty 29 | if (!productFlavors) productFlavors.add('') 30 | 31 | productFlavors.each { productFlavorName -> 32 | buildTypes.each { buildTypeName -> 33 | // Create variant and source names 34 | def sourceName = "${buildTypeName}" 35 | def targetName = "${sourceName.capitalize()}" 36 | if (productFlavorName) { 37 | sourceName = "${productFlavorName}${targetName}" 38 | } 39 | 40 | // React js bundle directories 41 | def jsBundleDirConfigName = "jsBundleDir${targetName}" 42 | def jsBundleDir = elvisFile(config."$jsBundleDirConfigName") ?: 43 | file("$buildDir/intermediates/assets/${sourceName}") 44 | 45 | def resourcesDirConfigName = "jsBundleDir${targetName}" 46 | def resourcesDir = elvisFile(config."${resourcesDirConfigName}") ?: 47 | file("$buildDir/intermediates/res/merged/${sourceName}") 48 | def jsBundleFile = file("$jsBundleDir/$bundleAssetName") 49 | 50 | // Bundle task name for variant 51 | def bundleJsAndAssetsTaskName = "bundle${targetName}JsAndAssets" 52 | 53 | def currentBundleTask = tasks.create( 54 | name: bundleJsAndAssetsTaskName, 55 | type: Exec) { 56 | group = "react" 57 | description = "bundle JS and assets for ${targetName}." 58 | 59 | // Create dirs if they are not there (e.g. the "clean" task just ran) 60 | doFirst { 61 | jsBundleDir.mkdirs() 62 | resourcesDir.mkdirs() 63 | } 64 | 65 | // Set up inputs and outputs so gradle can cache the result 66 | inputs.files fileTree(dir: reactRoot, excludes: inputExcludes) 67 | outputs.dir jsBundleDir 68 | outputs.dir resourcesDir 69 | 70 | // Set up the call to the react-native cli 71 | workingDir reactRoot 72 | 73 | // Set up dev mode 74 | def devEnabled = !targetName.toLowerCase().contains("release") 75 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 76 | commandLine "cmd", "/c", "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", 77 | "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir 78 | } else { 79 | commandLine "react-native", "bundle", "--platform", "android", "--dev", "${devEnabled}", 80 | "--entry-file", entryFile, "--bundle-output", jsBundleFile, "--assets-dest", resourcesDir 81 | } 82 | 83 | enabled config."bundleIn${targetName}" ?: targetName.toLowerCase().contains("release") 84 | } 85 | 86 | // Hook bundle${productFlavor}${buildType}JsAndAssets into the android build process 87 | currentBundleTask.dependsOn("merge${targetName}Resources") 88 | currentBundleTask.dependsOn("merge${targetName}Assets") 89 | 90 | runBefore("processArmeabi-v7a${targetName}Resources", currentBundleTask) 91 | runBefore("processX86${targetName}Resources", currentBundleTask) 92 | runBefore("processUniversal${targetName}Resources", currentBundleTask) 93 | runBefore("process${targetName}Resources", currentBundleTask) 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/samplern20/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.samplern20; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "SampleRN20"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/samplern20/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.samplern20; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.facebook.react.ReactNativeHost; 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.shell.MainReactPackage; 9 | import com.facebook.soloader.SoLoader; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | public class MainApplication extends Application implements ReactApplication { 15 | 16 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 17 | @Override 18 | public boolean getUseDeveloperSupport() { 19 | return BuildConfig.DEBUG; 20 | } 21 | 22 | @Override 23 | protected List getPackages() { 24 | return Arrays.asList( 25 | new MainReactPackage() 26 | ); 27 | } 28 | }; 29 | 30 | @Override 31 | public ReactNativeHost getReactNativeHost() { 32 | return mReactNativeHost; 33 | } 34 | 35 | @Override 36 | public void onCreate() { 37 | super.onCreate(); 38 | SoLoader.init(this, /* native exopackage */ false); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | SampleRN20 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.2.3' 9 | 10 | // NOTE: Do not place your application dependencies here; they belong 11 | // in the individual module build.gradle files 12 | } 13 | } 14 | 15 | allprojects { 16 | repositories { 17 | mavenLocal() 18 | jcenter() 19 | maven { 20 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 21 | url "$rootDir/../node_modules/react-native/android" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'SampleRN20' 2 | 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | 2 | import React, {Component} from 'react'; 3 | import {StyleSheet, View} from 'react-native'; 4 | import WebViewBridge from 'react-native-webview-bridge'; 5 | 6 | const injectScript = ` 7 | (function () { 8 | if (WebViewBridge) { 9 | 10 | WebViewBridge.onMessage = function (message) { 11 | if (message === "hello from react-native") { 12 | WebViewBridge.send("got the message inside webview"); 13 | } 14 | }; 15 | 16 | WebViewBridge.send("hello from webview"); 17 | } 18 | }()); 19 | `; 20 | 21 | export default class Sample2 extends Component { 22 | constructor(props) { 23 | super(props); 24 | this.onBridgeMessage = this.onBridgeMessage.bind(this); 25 | } 26 | 27 | onBridgeMessage(message) { 28 | switch (message) { 29 | case "hello from webview": 30 | this.webviewbridge.sendToBridge("hello from react-native"); 31 | break; 32 | case "got the message inside webview": 33 | console.log("we have got a message from webview! yeah"); 34 | break; 35 | } 36 | } 37 | 38 | render() { 39 | return ( 40 | 41 | this.webviewbridge = r} 43 | onBridgeMessage={this.onBridgeMessage} 44 | javaScriptEnabled={true} 45 | injectedJavaScript={injectScript} 46 | source={{uri: "https://google.com"}} 47 | /> 48 | 55 | 56 | ); 57 | } 58 | }; 59 | 60 | const styles = StyleSheet.create({ 61 | container: { 62 | flex: 1 63 | } 64 | }); 65 | 66 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SampleRN20", 3 | "displayName": "SampleRN20" 4 | } -------------------------------------------------------------------------------- /example/index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | */ 5 | 'use strict'; 6 | 7 | var React = require('react-native'); 8 | var { AppRegistry } = React; 9 | 10 | var Sample2 = require('./app'); 11 | 12 | AppRegistry.registerComponent('SampleRN20', () => Sample2); 13 | -------------------------------------------------------------------------------- /example/index.ios.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { AppRegistry } from 'react-native'; 3 | 4 | import Sample2 from './app'; 5 | 6 | AppRegistry.registerComponent('SampleRN20', () => Sample2); 7 | -------------------------------------------------------------------------------- /example/ios/SampleRN20.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; 11 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; 12 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; 13 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; 14 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; 15 | 00E356F31AD99517003FC87E /* SampleRN20Tests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* SampleRN20Tests.m */; }; 16 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; 17 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; 18 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; 19 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; 20 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; 21 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 22 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 23 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 24 | 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; 25 | 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; 26 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; 27 | D8263ECC1EAE0122005156F4 /* libReact-Native-Webview-Bridge.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D8263EC31EAE0114005156F4 /* libReact-Native-Webview-Bridge.a */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 34 | proxyType = 2; 35 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 36 | remoteInfo = RCTActionSheet; 37 | }; 38 | 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { 39 | isa = PBXContainerItemProxy; 40 | containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 41 | proxyType = 2; 42 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 43 | remoteInfo = RCTGeolocation; 44 | }; 45 | 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { 46 | isa = PBXContainerItemProxy; 47 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 48 | proxyType = 2; 49 | remoteGlobalIDString = 58B5115D1A9E6B3D00147676; 50 | remoteInfo = RCTImage; 51 | }; 52 | 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { 53 | isa = PBXContainerItemProxy; 54 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 55 | proxyType = 2; 56 | remoteGlobalIDString = 58B511DB1A9E6C8500147676; 57 | remoteInfo = RCTNetwork; 58 | }; 59 | 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { 60 | isa = PBXContainerItemProxy; 61 | containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 62 | proxyType = 2; 63 | remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; 64 | remoteInfo = RCTVibration; 65 | }; 66 | 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { 67 | isa = PBXContainerItemProxy; 68 | containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; 69 | proxyType = 1; 70 | remoteGlobalIDString = 13B07F861A680F5B00A75B9A; 71 | remoteInfo = SampleRN20; 72 | }; 73 | 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { 74 | isa = PBXContainerItemProxy; 75 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 76 | proxyType = 2; 77 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 78 | remoteInfo = RCTSettings; 79 | }; 80 | 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { 81 | isa = PBXContainerItemProxy; 82 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 83 | proxyType = 2; 84 | remoteGlobalIDString = 3C86DF461ADF2C930047B81A; 85 | remoteInfo = RCTWebSocket; 86 | }; 87 | 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { 88 | isa = PBXContainerItemProxy; 89 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 90 | proxyType = 2; 91 | remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; 92 | remoteInfo = React; 93 | }; 94 | 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { 95 | isa = PBXContainerItemProxy; 96 | containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 97 | proxyType = 2; 98 | remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; 99 | remoteInfo = "RCTImage-tvOS"; 100 | }; 101 | 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = { 102 | isa = PBXContainerItemProxy; 103 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 104 | proxyType = 2; 105 | remoteGlobalIDString = 2D2A28471D9B043800D4039D; 106 | remoteInfo = "RCTLinking-tvOS"; 107 | }; 108 | 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 109 | isa = PBXContainerItemProxy; 110 | containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 111 | proxyType = 2; 112 | remoteGlobalIDString = 2D2A28541D9B044C00D4039D; 113 | remoteInfo = "RCTNetwork-tvOS"; 114 | }; 115 | 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 116 | isa = PBXContainerItemProxy; 117 | containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 118 | proxyType = 2; 119 | remoteGlobalIDString = 2D2A28611D9B046600D4039D; 120 | remoteInfo = "RCTSettings-tvOS"; 121 | }; 122 | 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = { 123 | isa = PBXContainerItemProxy; 124 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 125 | proxyType = 2; 126 | remoteGlobalIDString = 2D2A287B1D9B048500D4039D; 127 | remoteInfo = "RCTText-tvOS"; 128 | }; 129 | 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = { 130 | isa = PBXContainerItemProxy; 131 | containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 132 | proxyType = 2; 133 | remoteGlobalIDString = 2D2A28881D9B049200D4039D; 134 | remoteInfo = "RCTWebSocket-tvOS"; 135 | }; 136 | 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = { 137 | isa = PBXContainerItemProxy; 138 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 139 | proxyType = 2; 140 | remoteGlobalIDString = 2D2A28131D9B038B00D4039D; 141 | remoteInfo = "React-tvOS"; 142 | }; 143 | 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = { 144 | isa = PBXContainerItemProxy; 145 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 146 | proxyType = 2; 147 | remoteGlobalIDString = 3D3C059A1DE3340900C268FA; 148 | remoteInfo = yoga; 149 | }; 150 | 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = { 151 | isa = PBXContainerItemProxy; 152 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 153 | proxyType = 2; 154 | remoteGlobalIDString = 3D3C06751DE3340C00C268FA; 155 | remoteInfo = "yoga-tvOS"; 156 | }; 157 | 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { 158 | isa = PBXContainerItemProxy; 159 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 160 | proxyType = 2; 161 | remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; 162 | remoteInfo = cxxreact; 163 | }; 164 | 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 165 | isa = PBXContainerItemProxy; 166 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 167 | proxyType = 2; 168 | remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; 169 | remoteInfo = "cxxreact-tvOS"; 170 | }; 171 | 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 172 | isa = PBXContainerItemProxy; 173 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 174 | proxyType = 2; 175 | remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; 176 | remoteInfo = jschelpers; 177 | }; 178 | 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = { 179 | isa = PBXContainerItemProxy; 180 | containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; 181 | proxyType = 2; 182 | remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; 183 | remoteInfo = "jschelpers-tvOS"; 184 | }; 185 | 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { 186 | isa = PBXContainerItemProxy; 187 | containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; 188 | proxyType = 2; 189 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 190 | remoteInfo = RCTAnimation; 191 | }; 192 | 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { 193 | isa = PBXContainerItemProxy; 194 | containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; 195 | proxyType = 2; 196 | remoteGlobalIDString = 2D2A28201D9B03D100D4039D; 197 | remoteInfo = "RCTAnimation-tvOS"; 198 | }; 199 | 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { 200 | isa = PBXContainerItemProxy; 201 | containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 202 | proxyType = 2; 203 | remoteGlobalIDString = 134814201AA4EA6300B7C361; 204 | remoteInfo = RCTLinking; 205 | }; 206 | 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { 207 | isa = PBXContainerItemProxy; 208 | containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 209 | proxyType = 2; 210 | remoteGlobalIDString = 58B5119B1A9E6C1200147676; 211 | remoteInfo = RCTText; 212 | }; 213 | D8263EC21EAE0114005156F4 /* PBXContainerItemProxy */ = { 214 | isa = PBXContainerItemProxy; 215 | containerPortal = D8263EAD1EAE0114005156F4 /* React-Native-Webview-Bridge.xcodeproj */; 216 | proxyType = 2; 217 | remoteGlobalIDString = 4114DC4C1C187C3A003CD988; 218 | remoteInfo = "React-Native-Webview-Bridge"; 219 | }; 220 | /* End PBXContainerItemProxy section */ 221 | 222 | /* Begin PBXFileReference section */ 223 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; 224 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; 225 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; 226 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; 227 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; 228 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; 229 | 00E356EE1AD99517003FC87E /* SampleRN20Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleRN20Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 230 | 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 231 | 00E356F21AD99517003FC87E /* SampleRN20Tests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleRN20Tests.m; sourceTree = ""; }; 232 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; 233 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; 234 | 13B07F961A680F5B00A75B9A /* SampleRN20.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleRN20.app; sourceTree = BUILT_PRODUCTS_DIR; }; 235 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = SampleRN20/AppDelegate.h; sourceTree = ""; }; 236 | 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = SampleRN20/AppDelegate.m; sourceTree = ""; }; 237 | 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 238 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = SampleRN20/Images.xcassets; sourceTree = ""; }; 239 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = SampleRN20/Info.plist; sourceTree = ""; }; 240 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = SampleRN20/main.m; sourceTree = ""; }; 241 | 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; 242 | 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; 243 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; 244 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; 245 | D8263EAD1EAE0114005156F4 /* 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 = ""; }; 246 | /* End PBXFileReference section */ 247 | 248 | /* Begin PBXFrameworksBuildPhase section */ 249 | 00E356EB1AD99517003FC87E /* Frameworks */ = { 250 | isa = PBXFrameworksBuildPhase; 251 | buildActionMask = 2147483647; 252 | files = ( 253 | 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | }; 257 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { 258 | isa = PBXFrameworksBuildPhase; 259 | buildActionMask = 2147483647; 260 | files = ( 261 | D8263ECC1EAE0122005156F4 /* libReact-Native-Webview-Bridge.a in Frameworks */, 262 | 146834051AC3E58100842450 /* libReact.a in Frameworks */, 263 | 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, 264 | 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, 265 | 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, 266 | 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, 267 | 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, 268 | 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, 269 | 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, 270 | 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, 271 | 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, 272 | 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, 273 | ); 274 | runOnlyForDeploymentPostprocessing = 0; 275 | }; 276 | /* End PBXFrameworksBuildPhase section */ 277 | 278 | /* Begin PBXGroup section */ 279 | 00C302A81ABCB8CE00DB3ED1 /* Products */ = { 280 | isa = PBXGroup; 281 | children = ( 282 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, 283 | ); 284 | name = Products; 285 | sourceTree = ""; 286 | }; 287 | 00C302B61ABCB90400DB3ED1 /* Products */ = { 288 | isa = PBXGroup; 289 | children = ( 290 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, 291 | ); 292 | name = Products; 293 | sourceTree = ""; 294 | }; 295 | 00C302BC1ABCB91800DB3ED1 /* Products */ = { 296 | isa = PBXGroup; 297 | children = ( 298 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, 299 | 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, 300 | ); 301 | name = Products; 302 | sourceTree = ""; 303 | }; 304 | 00C302D41ABCB9D200DB3ED1 /* Products */ = { 305 | isa = PBXGroup; 306 | children = ( 307 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, 308 | 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, 309 | ); 310 | name = Products; 311 | sourceTree = ""; 312 | }; 313 | 00C302E01ABCB9EE00DB3ED1 /* Products */ = { 314 | isa = PBXGroup; 315 | children = ( 316 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, 317 | ); 318 | name = Products; 319 | sourceTree = ""; 320 | }; 321 | 00E356EF1AD99517003FC87E /* SampleRN20Tests */ = { 322 | isa = PBXGroup; 323 | children = ( 324 | 00E356F21AD99517003FC87E /* SampleRN20Tests.m */, 325 | 00E356F01AD99517003FC87E /* Supporting Files */, 326 | ); 327 | path = SampleRN20Tests; 328 | sourceTree = ""; 329 | }; 330 | 00E356F01AD99517003FC87E /* Supporting Files */ = { 331 | isa = PBXGroup; 332 | children = ( 333 | 00E356F11AD99517003FC87E /* Info.plist */, 334 | ); 335 | name = "Supporting Files"; 336 | sourceTree = ""; 337 | }; 338 | 139105B71AF99BAD00B5F7CC /* Products */ = { 339 | isa = PBXGroup; 340 | children = ( 341 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, 342 | 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, 343 | ); 344 | name = Products; 345 | sourceTree = ""; 346 | }; 347 | 139FDEE71B06529A00C62182 /* Products */ = { 348 | isa = PBXGroup; 349 | children = ( 350 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 351 | 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, 352 | ); 353 | name = Products; 354 | sourceTree = ""; 355 | }; 356 | 13B07FAE1A68108700A75B9A /* SampleRN20 */ = { 357 | isa = PBXGroup; 358 | children = ( 359 | 008F07F21AC5B25A0029DE68 /* main.jsbundle */, 360 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */, 361 | 13B07FB01A68108700A75B9A /* AppDelegate.m */, 362 | 13B07FB51A68108700A75B9A /* Images.xcassets */, 363 | 13B07FB61A68108700A75B9A /* Info.plist */, 364 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, 365 | 13B07FB71A68108700A75B9A /* main.m */, 366 | ); 367 | name = SampleRN20; 368 | sourceTree = ""; 369 | }; 370 | 146834001AC3E56700842450 /* Products */ = { 371 | isa = PBXGroup; 372 | children = ( 373 | 146834041AC3E56700842450 /* libReact.a */, 374 | 3DAD3EA31DF850E9000B6D8A /* libReact.a */, 375 | 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, 376 | 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, 377 | 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, 378 | 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, 379 | 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, 380 | 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, 381 | ); 382 | name = Products; 383 | sourceTree = ""; 384 | }; 385 | 5E91572E1DD0AC6500FF2AA8 /* Products */ = { 386 | isa = PBXGroup; 387 | children = ( 388 | 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, 389 | 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */, 390 | ); 391 | name = Products; 392 | sourceTree = ""; 393 | }; 394 | 78C398B11ACF4ADC00677621 /* Products */ = { 395 | isa = PBXGroup; 396 | children = ( 397 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, 398 | 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, 399 | ); 400 | name = Products; 401 | sourceTree = ""; 402 | }; 403 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = { 404 | isa = PBXGroup; 405 | children = ( 406 | D8263EAD1EAE0114005156F4 /* React-Native-Webview-Bridge.xcodeproj */, 407 | 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, 408 | 146833FF1AC3E56700842450 /* React.xcodeproj */, 409 | 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, 410 | 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, 411 | 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, 412 | 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, 413 | 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, 414 | 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, 415 | 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, 416 | 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, 417 | 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, 418 | ); 419 | name = Libraries; 420 | sourceTree = ""; 421 | }; 422 | 832341B11AAA6A8300B99B32 /* Products */ = { 423 | isa = PBXGroup; 424 | children = ( 425 | 832341B51AAA6A8300B99B32 /* libRCTText.a */, 426 | 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, 427 | ); 428 | name = Products; 429 | sourceTree = ""; 430 | }; 431 | 83CBB9F61A601CBA00E9B192 = { 432 | isa = PBXGroup; 433 | children = ( 434 | 13B07FAE1A68108700A75B9A /* SampleRN20 */, 435 | 832341AE1AAA6A7D00B99B32 /* Libraries */, 436 | 00E356EF1AD99517003FC87E /* SampleRN20Tests */, 437 | 83CBBA001A601CBA00E9B192 /* Products */, 438 | ); 439 | indentWidth = 2; 440 | sourceTree = ""; 441 | tabWidth = 2; 442 | }; 443 | 83CBBA001A601CBA00E9B192 /* Products */ = { 444 | isa = PBXGroup; 445 | children = ( 446 | 13B07F961A680F5B00A75B9A /* SampleRN20.app */, 447 | 00E356EE1AD99517003FC87E /* SampleRN20Tests.xctest */, 448 | ); 449 | name = Products; 450 | sourceTree = ""; 451 | }; 452 | D8263EAE1EAE0114005156F4 /* Products */ = { 453 | isa = PBXGroup; 454 | children = ( 455 | D8263EC31EAE0114005156F4 /* libReact-Native-Webview-Bridge.a */, 456 | ); 457 | name = Products; 458 | sourceTree = ""; 459 | }; 460 | /* End PBXGroup section */ 461 | 462 | /* Begin PBXNativeTarget section */ 463 | 00E356ED1AD99517003FC87E /* SampleRN20Tests */ = { 464 | isa = PBXNativeTarget; 465 | buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SampleRN20Tests" */; 466 | buildPhases = ( 467 | 00E356EA1AD99517003FC87E /* Sources */, 468 | 00E356EB1AD99517003FC87E /* Frameworks */, 469 | 00E356EC1AD99517003FC87E /* Resources */, 470 | ); 471 | buildRules = ( 472 | ); 473 | dependencies = ( 474 | 00E356F51AD99517003FC87E /* PBXTargetDependency */, 475 | ); 476 | name = SampleRN20Tests; 477 | productName = SampleRN20Tests; 478 | productReference = 00E356EE1AD99517003FC87E /* SampleRN20Tests.xctest */; 479 | productType = "com.apple.product-type.bundle.unit-test"; 480 | }; 481 | 13B07F861A680F5B00A75B9A /* SampleRN20 */ = { 482 | isa = PBXNativeTarget; 483 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SampleRN20" */; 484 | buildPhases = ( 485 | 13B07F871A680F5B00A75B9A /* Sources */, 486 | 13B07F8C1A680F5B00A75B9A /* Frameworks */, 487 | 13B07F8E1A680F5B00A75B9A /* Resources */, 488 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 489 | ); 490 | buildRules = ( 491 | ); 492 | dependencies = ( 493 | ); 494 | name = SampleRN20; 495 | productName = "Hello World"; 496 | productReference = 13B07F961A680F5B00A75B9A /* SampleRN20.app */; 497 | productType = "com.apple.product-type.application"; 498 | }; 499 | /* End PBXNativeTarget section */ 500 | 501 | /* Begin PBXProject section */ 502 | 83CBB9F71A601CBA00E9B192 /* Project object */ = { 503 | isa = PBXProject; 504 | attributes = { 505 | LastUpgradeCheck = 0610; 506 | ORGANIZATIONNAME = Facebook; 507 | TargetAttributes = { 508 | 00E356ED1AD99517003FC87E = { 509 | CreatedOnToolsVersion = 6.2; 510 | TestTargetID = 13B07F861A680F5B00A75B9A; 511 | }; 512 | }; 513 | }; 514 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SampleRN20" */; 515 | compatibilityVersion = "Xcode 3.2"; 516 | developmentRegion = English; 517 | hasScannedForEncodings = 0; 518 | knownRegions = ( 519 | en, 520 | Base, 521 | ); 522 | mainGroup = 83CBB9F61A601CBA00E9B192; 523 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; 524 | projectDirPath = ""; 525 | projectReferences = ( 526 | { 527 | ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; 528 | ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; 529 | }, 530 | { 531 | ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; 532 | ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; 533 | }, 534 | { 535 | ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; 536 | ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; 537 | }, 538 | { 539 | ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; 540 | ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; 541 | }, 542 | { 543 | ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; 544 | ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; 545 | }, 546 | { 547 | ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; 548 | ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; 549 | }, 550 | { 551 | ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; 552 | ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; 553 | }, 554 | { 555 | ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; 556 | ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; 557 | }, 558 | { 559 | ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; 560 | ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; 561 | }, 562 | { 563 | ProductGroup = 139FDEE71B06529A00C62182 /* Products */; 564 | ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; 565 | }, 566 | { 567 | ProductGroup = D8263EAE1EAE0114005156F4 /* Products */; 568 | ProjectRef = D8263EAD1EAE0114005156F4 /* React-Native-Webview-Bridge.xcodeproj */; 569 | }, 570 | { 571 | ProductGroup = 146834001AC3E56700842450 /* Products */; 572 | ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; 573 | }, 574 | ); 575 | projectRoot = ""; 576 | targets = ( 577 | 13B07F861A680F5B00A75B9A /* SampleRN20 */, 578 | 00E356ED1AD99517003FC87E /* SampleRN20Tests */, 579 | ); 580 | }; 581 | /* End PBXProject section */ 582 | 583 | /* Begin PBXReferenceProxy section */ 584 | 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { 585 | isa = PBXReferenceProxy; 586 | fileType = archive.ar; 587 | path = libRCTActionSheet.a; 588 | remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; 589 | sourceTree = BUILT_PRODUCTS_DIR; 590 | }; 591 | 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { 592 | isa = PBXReferenceProxy; 593 | fileType = archive.ar; 594 | path = libRCTGeolocation.a; 595 | remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; 596 | sourceTree = BUILT_PRODUCTS_DIR; 597 | }; 598 | 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { 599 | isa = PBXReferenceProxy; 600 | fileType = archive.ar; 601 | path = libRCTImage.a; 602 | remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; 603 | sourceTree = BUILT_PRODUCTS_DIR; 604 | }; 605 | 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { 606 | isa = PBXReferenceProxy; 607 | fileType = archive.ar; 608 | path = libRCTNetwork.a; 609 | remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; 610 | sourceTree = BUILT_PRODUCTS_DIR; 611 | }; 612 | 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { 613 | isa = PBXReferenceProxy; 614 | fileType = archive.ar; 615 | path = libRCTVibration.a; 616 | remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; 617 | sourceTree = BUILT_PRODUCTS_DIR; 618 | }; 619 | 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { 620 | isa = PBXReferenceProxy; 621 | fileType = archive.ar; 622 | path = libRCTSettings.a; 623 | remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; 624 | sourceTree = BUILT_PRODUCTS_DIR; 625 | }; 626 | 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { 627 | isa = PBXReferenceProxy; 628 | fileType = archive.ar; 629 | path = libRCTWebSocket.a; 630 | remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; 631 | sourceTree = BUILT_PRODUCTS_DIR; 632 | }; 633 | 146834041AC3E56700842450 /* libReact.a */ = { 634 | isa = PBXReferenceProxy; 635 | fileType = archive.ar; 636 | path = libReact.a; 637 | remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; 638 | sourceTree = BUILT_PRODUCTS_DIR; 639 | }; 640 | 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = { 641 | isa = PBXReferenceProxy; 642 | fileType = archive.ar; 643 | path = "libRCTImage-tvOS.a"; 644 | remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */; 645 | sourceTree = BUILT_PRODUCTS_DIR; 646 | }; 647 | 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = { 648 | isa = PBXReferenceProxy; 649 | fileType = archive.ar; 650 | path = "libRCTLinking-tvOS.a"; 651 | remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */; 652 | sourceTree = BUILT_PRODUCTS_DIR; 653 | }; 654 | 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = { 655 | isa = PBXReferenceProxy; 656 | fileType = archive.ar; 657 | path = "libRCTNetwork-tvOS.a"; 658 | remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */; 659 | sourceTree = BUILT_PRODUCTS_DIR; 660 | }; 661 | 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = { 662 | isa = PBXReferenceProxy; 663 | fileType = archive.ar; 664 | path = "libRCTSettings-tvOS.a"; 665 | remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */; 666 | sourceTree = BUILT_PRODUCTS_DIR; 667 | }; 668 | 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = { 669 | isa = PBXReferenceProxy; 670 | fileType = archive.ar; 671 | path = "libRCTText-tvOS.a"; 672 | remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */; 673 | sourceTree = BUILT_PRODUCTS_DIR; 674 | }; 675 | 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = { 676 | isa = PBXReferenceProxy; 677 | fileType = archive.ar; 678 | path = "libRCTWebSocket-tvOS.a"; 679 | remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */; 680 | sourceTree = BUILT_PRODUCTS_DIR; 681 | }; 682 | 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = { 683 | isa = PBXReferenceProxy; 684 | fileType = archive.ar; 685 | path = libReact.a; 686 | remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */; 687 | sourceTree = BUILT_PRODUCTS_DIR; 688 | }; 689 | 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = { 690 | isa = PBXReferenceProxy; 691 | fileType = archive.ar; 692 | path = libyoga.a; 693 | remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */; 694 | sourceTree = BUILT_PRODUCTS_DIR; 695 | }; 696 | 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = { 697 | isa = PBXReferenceProxy; 698 | fileType = archive.ar; 699 | path = libyoga.a; 700 | remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; 701 | sourceTree = BUILT_PRODUCTS_DIR; 702 | }; 703 | 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { 704 | isa = PBXReferenceProxy; 705 | fileType = archive.ar; 706 | path = libcxxreact.a; 707 | remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; 708 | sourceTree = BUILT_PRODUCTS_DIR; 709 | }; 710 | 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { 711 | isa = PBXReferenceProxy; 712 | fileType = archive.ar; 713 | path = libcxxreact.a; 714 | remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */; 715 | sourceTree = BUILT_PRODUCTS_DIR; 716 | }; 717 | 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = { 718 | isa = PBXReferenceProxy; 719 | fileType = archive.ar; 720 | path = libjschelpers.a; 721 | remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */; 722 | sourceTree = BUILT_PRODUCTS_DIR; 723 | }; 724 | 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = { 725 | isa = PBXReferenceProxy; 726 | fileType = archive.ar; 727 | path = libjschelpers.a; 728 | remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; 729 | sourceTree = BUILT_PRODUCTS_DIR; 730 | }; 731 | 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { 732 | isa = PBXReferenceProxy; 733 | fileType = archive.ar; 734 | path = libRCTAnimation.a; 735 | remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; 736 | sourceTree = BUILT_PRODUCTS_DIR; 737 | }; 738 | 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */ = { 739 | isa = PBXReferenceProxy; 740 | fileType = archive.ar; 741 | path = "libRCTAnimation-tvOS.a"; 742 | remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; 743 | sourceTree = BUILT_PRODUCTS_DIR; 744 | }; 745 | 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { 746 | isa = PBXReferenceProxy; 747 | fileType = archive.ar; 748 | path = libRCTLinking.a; 749 | remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; 750 | sourceTree = BUILT_PRODUCTS_DIR; 751 | }; 752 | 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { 753 | isa = PBXReferenceProxy; 754 | fileType = archive.ar; 755 | path = libRCTText.a; 756 | remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; 757 | sourceTree = BUILT_PRODUCTS_DIR; 758 | }; 759 | D8263EC31EAE0114005156F4 /* libReact-Native-Webview-Bridge.a */ = { 760 | isa = PBXReferenceProxy; 761 | fileType = archive.ar; 762 | path = "libReact-Native-Webview-Bridge.a"; 763 | remoteRef = D8263EC21EAE0114005156F4 /* PBXContainerItemProxy */; 764 | sourceTree = BUILT_PRODUCTS_DIR; 765 | }; 766 | /* End PBXReferenceProxy section */ 767 | 768 | /* Begin PBXResourcesBuildPhase section */ 769 | 00E356EC1AD99517003FC87E /* Resources */ = { 770 | isa = PBXResourcesBuildPhase; 771 | buildActionMask = 2147483647; 772 | files = ( 773 | ); 774 | runOnlyForDeploymentPostprocessing = 0; 775 | }; 776 | 13B07F8E1A680F5B00A75B9A /* Resources */ = { 777 | isa = PBXResourcesBuildPhase; 778 | buildActionMask = 2147483647; 779 | files = ( 780 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 781 | 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, 782 | ); 783 | runOnlyForDeploymentPostprocessing = 0; 784 | }; 785 | /* End PBXResourcesBuildPhase section */ 786 | 787 | /* Begin PBXShellScriptBuildPhase section */ 788 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { 789 | isa = PBXShellScriptBuildPhase; 790 | buildActionMask = 2147483647; 791 | files = ( 792 | ); 793 | inputPaths = ( 794 | ); 795 | name = "Bundle React Native code and images"; 796 | outputPaths = ( 797 | ); 798 | runOnlyForDeploymentPostprocessing = 0; 799 | shellPath = /bin/sh; 800 | shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; 801 | }; 802 | /* End PBXShellScriptBuildPhase section */ 803 | 804 | /* Begin PBXSourcesBuildPhase section */ 805 | 00E356EA1AD99517003FC87E /* Sources */ = { 806 | isa = PBXSourcesBuildPhase; 807 | buildActionMask = 2147483647; 808 | files = ( 809 | 00E356F31AD99517003FC87E /* SampleRN20Tests.m in Sources */, 810 | ); 811 | runOnlyForDeploymentPostprocessing = 0; 812 | }; 813 | 13B07F871A680F5B00A75B9A /* Sources */ = { 814 | isa = PBXSourcesBuildPhase; 815 | buildActionMask = 2147483647; 816 | files = ( 817 | 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, 818 | 13B07FC11A68108700A75B9A /* main.m in Sources */, 819 | ); 820 | runOnlyForDeploymentPostprocessing = 0; 821 | }; 822 | /* End PBXSourcesBuildPhase section */ 823 | 824 | /* Begin PBXTargetDependency section */ 825 | 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { 826 | isa = PBXTargetDependency; 827 | target = 13B07F861A680F5B00A75B9A /* SampleRN20 */; 828 | targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; 829 | }; 830 | /* End PBXTargetDependency section */ 831 | 832 | /* Begin PBXVariantGroup section */ 833 | 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { 834 | isa = PBXVariantGroup; 835 | children = ( 836 | 13B07FB21A68108700A75B9A /* Base */, 837 | ); 838 | name = LaunchScreen.xib; 839 | path = SampleRN20; 840 | sourceTree = ""; 841 | }; 842 | /* End PBXVariantGroup section */ 843 | 844 | /* Begin XCBuildConfiguration section */ 845 | 00E356F61AD99517003FC87E /* Debug */ = { 846 | isa = XCBuildConfiguration; 847 | buildSettings = { 848 | BUNDLE_LOADER = "$(TEST_HOST)"; 849 | GCC_PREPROCESSOR_DEFINITIONS = ( 850 | "DEBUG=1", 851 | "$(inherited)", 852 | ); 853 | INFOPLIST_FILE = SampleRN20Tests/Info.plist; 854 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 855 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 856 | OTHER_LDFLAGS = ( 857 | "-ObjC", 858 | "-lc++", 859 | ); 860 | PRODUCT_NAME = "$(TARGET_NAME)"; 861 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SampleRN20.app/SampleRN20"; 862 | }; 863 | name = Debug; 864 | }; 865 | 00E356F71AD99517003FC87E /* Release */ = { 866 | isa = XCBuildConfiguration; 867 | buildSettings = { 868 | BUNDLE_LOADER = "$(TEST_HOST)"; 869 | COPY_PHASE_STRIP = NO; 870 | INFOPLIST_FILE = SampleRN20Tests/Info.plist; 871 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 872 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 873 | OTHER_LDFLAGS = ( 874 | "-ObjC", 875 | "-lc++", 876 | ); 877 | PRODUCT_NAME = "$(TARGET_NAME)"; 878 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SampleRN20.app/SampleRN20"; 879 | }; 880 | name = Release; 881 | }; 882 | 13B07F941A680F5B00A75B9A /* Debug */ = { 883 | isa = XCBuildConfiguration; 884 | buildSettings = { 885 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 886 | CURRENT_PROJECT_VERSION = 1; 887 | DEAD_CODE_STRIPPING = NO; 888 | INFOPLIST_FILE = SampleRN20/Info.plist; 889 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 890 | OTHER_LDFLAGS = ( 891 | "$(inherited)", 892 | "-ObjC", 893 | "-lc++", 894 | ); 895 | PRODUCT_NAME = SampleRN20; 896 | VERSIONING_SYSTEM = "apple-generic"; 897 | }; 898 | name = Debug; 899 | }; 900 | 13B07F951A680F5B00A75B9A /* Release */ = { 901 | isa = XCBuildConfiguration; 902 | buildSettings = { 903 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 904 | CURRENT_PROJECT_VERSION = 1; 905 | INFOPLIST_FILE = SampleRN20/Info.plist; 906 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 907 | OTHER_LDFLAGS = ( 908 | "$(inherited)", 909 | "-ObjC", 910 | "-lc++", 911 | ); 912 | PRODUCT_NAME = SampleRN20; 913 | VERSIONING_SYSTEM = "apple-generic"; 914 | }; 915 | name = Release; 916 | }; 917 | 83CBBA201A601CBA00E9B192 /* Debug */ = { 918 | isa = XCBuildConfiguration; 919 | buildSettings = { 920 | ALWAYS_SEARCH_USER_PATHS = NO; 921 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 922 | CLANG_CXX_LIBRARY = "libc++"; 923 | CLANG_ENABLE_MODULES = YES; 924 | CLANG_ENABLE_OBJC_ARC = YES; 925 | CLANG_WARN_BOOL_CONVERSION = YES; 926 | CLANG_WARN_CONSTANT_CONVERSION = YES; 927 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 928 | CLANG_WARN_EMPTY_BODY = YES; 929 | CLANG_WARN_ENUM_CONVERSION = YES; 930 | CLANG_WARN_INT_CONVERSION = YES; 931 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 932 | CLANG_WARN_UNREACHABLE_CODE = YES; 933 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 934 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 935 | COPY_PHASE_STRIP = NO; 936 | ENABLE_STRICT_OBJC_MSGSEND = YES; 937 | GCC_C_LANGUAGE_STANDARD = gnu99; 938 | GCC_DYNAMIC_NO_PIC = NO; 939 | GCC_OPTIMIZATION_LEVEL = 0; 940 | GCC_PREPROCESSOR_DEFINITIONS = ( 941 | "DEBUG=1", 942 | "$(inherited)", 943 | ); 944 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 945 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 946 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 947 | GCC_WARN_UNDECLARED_SELECTOR = YES; 948 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 949 | GCC_WARN_UNUSED_FUNCTION = YES; 950 | GCC_WARN_UNUSED_VARIABLE = YES; 951 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 952 | MTL_ENABLE_DEBUG_INFO = YES; 953 | ONLY_ACTIVE_ARCH = YES; 954 | SDKROOT = iphoneos; 955 | }; 956 | name = Debug; 957 | }; 958 | 83CBBA211A601CBA00E9B192 /* Release */ = { 959 | isa = XCBuildConfiguration; 960 | buildSettings = { 961 | ALWAYS_SEARCH_USER_PATHS = NO; 962 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 963 | CLANG_CXX_LIBRARY = "libc++"; 964 | CLANG_ENABLE_MODULES = YES; 965 | CLANG_ENABLE_OBJC_ARC = YES; 966 | CLANG_WARN_BOOL_CONVERSION = YES; 967 | CLANG_WARN_CONSTANT_CONVERSION = YES; 968 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 969 | CLANG_WARN_EMPTY_BODY = YES; 970 | CLANG_WARN_ENUM_CONVERSION = YES; 971 | CLANG_WARN_INT_CONVERSION = YES; 972 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 973 | CLANG_WARN_UNREACHABLE_CODE = YES; 974 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 975 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 976 | COPY_PHASE_STRIP = YES; 977 | ENABLE_NS_ASSERTIONS = NO; 978 | ENABLE_STRICT_OBJC_MSGSEND = YES; 979 | GCC_C_LANGUAGE_STANDARD = gnu99; 980 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 981 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 982 | GCC_WARN_UNDECLARED_SELECTOR = YES; 983 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 984 | GCC_WARN_UNUSED_FUNCTION = YES; 985 | GCC_WARN_UNUSED_VARIABLE = YES; 986 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 987 | MTL_ENABLE_DEBUG_INFO = NO; 988 | SDKROOT = iphoneos; 989 | VALIDATE_PRODUCT = YES; 990 | }; 991 | name = Release; 992 | }; 993 | /* End XCBuildConfiguration section */ 994 | 995 | /* Begin XCConfigurationList section */ 996 | 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "SampleRN20Tests" */ = { 997 | isa = XCConfigurationList; 998 | buildConfigurations = ( 999 | 00E356F61AD99517003FC87E /* Debug */, 1000 | 00E356F71AD99517003FC87E /* Release */, 1001 | ); 1002 | defaultConfigurationIsVisible = 0; 1003 | defaultConfigurationName = Release; 1004 | }; 1005 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "SampleRN20" */ = { 1006 | isa = XCConfigurationList; 1007 | buildConfigurations = ( 1008 | 13B07F941A680F5B00A75B9A /* Debug */, 1009 | 13B07F951A680F5B00A75B9A /* Release */, 1010 | ); 1011 | defaultConfigurationIsVisible = 0; 1012 | defaultConfigurationName = Release; 1013 | }; 1014 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "SampleRN20" */ = { 1015 | isa = XCConfigurationList; 1016 | buildConfigurations = ( 1017 | 83CBBA201A601CBA00E9B192 /* Debug */, 1018 | 83CBBA211A601CBA00E9B192 /* Release */, 1019 | ); 1020 | defaultConfigurationIsVisible = 0; 1021 | defaultConfigurationName = Release; 1022 | }; 1023 | /* End XCConfigurationList section */ 1024 | }; 1025 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; 1026 | } 1027 | -------------------------------------------------------------------------------- /example/ios/SampleRN20.xcodeproj/xcshareddata/xcschemes/SampleRN20-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/ios/SampleRN20.xcodeproj/xcshareddata/xcschemes/SampleRN20.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/ios/SampleRN20/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /example/ios/SampleRN20/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | 15 | @implementation AppDelegate 16 | 17 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 18 | { 19 | NSURL *jsCodeLocation; 20 | 21 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil]; 22 | 23 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 24 | moduleName:@"SampleRN20" 25 | initialProperties:nil 26 | launchOptions:launchOptions]; 27 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 28 | 29 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 30 | UIViewController *rootViewController = [UIViewController new]; 31 | rootViewController.view = rootView; 32 | self.window.rootViewController = rootViewController; 33 | [self.window makeKeyAndVisible]; 34 | return YES; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /example/ios/SampleRN20/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/ios/SampleRN20/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /example/ios/SampleRN20/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | SampleRN20 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UIViewControllerBasedStatusBarAppearance 40 | 41 | NSLocationWhenInUseUsageDescription 42 | 43 | NSAppTransportSecurity 44 | 45 | 46 | NSExceptionDomains 47 | 48 | localhost 49 | 50 | NSExceptionAllowsInsecureHTTPLoads 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /example/ios/SampleRN20/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/ios/SampleRN20Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/SampleRN20Tests/SampleRN20Tests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface SampleRN20Tests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation SampleRN20Tests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[[UIApplication sharedApplication] delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SampleRN20", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start" 7 | }, 8 | "dependencies": { 9 | "react": "16.0.0-alpha.6", 10 | "react-native": "0.43.4", 11 | "react-native-webview-bridge": "file:../" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /example/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Hello test html 8 | 9 | -------------------------------------------------------------------------------- /ios/RCTWebViewBridge.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz) 6 | * All rights reserved 7 | * 8 | * This source code is licensed under the BSD-style license found in the 9 | * LICENSE file in the root directory of this source tree. An additional grant 10 | * of patent rights can be found in the PATENTS file in the same directory. 11 | */ 12 | 13 | #if __has_include() 14 | #import 15 | #else 16 | #import "RCTView.h" 17 | #endif 18 | 19 | @class RCTWebViewBridge; 20 | 21 | /** 22 | * Special scheme used to pass messages to the injectedJavaScript 23 | * code without triggering a page load. Usage: 24 | * 25 | * window.location.href = RCTJSNavigationScheme + '://hello' 26 | */ 27 | extern NSString *const RCTJSNavigationScheme; 28 | 29 | @protocol RCTWebViewBridgeDelegate 30 | 31 | - (BOOL)webView:(RCTWebViewBridge *)webView 32 | shouldStartLoadForRequest:(NSMutableDictionary *)request 33 | withCallback:(RCTDirectEventBlock)callback; 34 | 35 | @end 36 | 37 | @interface RCTWebViewBridge : RCTView 38 | 39 | @property (nonatomic, weak) id delegate; 40 | 41 | @property (nonatomic, copy) NSDictionary *source; 42 | @property (nonatomic, assign) UIEdgeInsets contentInset; 43 | @property (nonatomic, assign) BOOL automaticallyAdjustContentInsets; 44 | @property (nonatomic, assign) BOOL hideKeyboardAccessoryView; 45 | @property (nonatomic, copy) NSString *injectedJavaScript; 46 | 47 | - (void)goForward; 48 | - (void)goBack; 49 | - (void)reload; 50 | - (void)sendToBridge:(NSString *)message; 51 | 52 | @end 53 | -------------------------------------------------------------------------------- /ios/RCTWebViewBridge.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz) 6 | * All rights reserved 7 | * 8 | * This source code is licensed under the BSD-style license found in the 9 | * LICENSE file in the root directory of this source tree. An additional grant 10 | * of patent rights can be found in the PATENTS file in the same directory. 11 | */ 12 | 13 | #import "RCTWebViewBridge.h" 14 | 15 | #import 16 | 17 | #if __has_include() 18 | #import 19 | #else 20 | #import "RCTAutoInsetsProtocol.h" 21 | #endif 22 | 23 | #if __has_include() 24 | #import 25 | #else 26 | #import "RCTConvert.h" 27 | #endif 28 | 29 | #if __has_include() 30 | #import 31 | #else 32 | #import "RCTEventDispatcher.h" 33 | #endif 34 | 35 | #if __has_include() 36 | #import 37 | #else 38 | #import "RCTLog.h" 39 | #endif 40 | 41 | #if __has_include() 42 | #import 43 | #else 44 | #import "RCTUtils.h" 45 | #endif 46 | 47 | #if __has_include() 48 | #import 49 | #else 50 | #import "UIView+React.h" 51 | #endif 52 | 53 | #import 54 | 55 | //This is a very elegent way of defining multiline string in objective-c. 56 | //source: http://stackoverflow.com/a/23387659/828487 57 | #define NSStringMultiline(...) [[NSString alloc] initWithCString:#__VA_ARGS__ encoding:NSUTF8StringEncoding] 58 | 59 | //we don'e need this one since it has been defined in RCTWebView.m 60 | //NSString *const RCTJSNavigationScheme = @"react-js-navigation"; 61 | NSString *const RCTWebViewBridgeSchema = @"wvb"; 62 | 63 | // runtime trick to remove UIWebview keyboard default toolbar 64 | // see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279 65 | @interface _SwizzleHelper : NSObject @end 66 | @implementation _SwizzleHelper 67 | -(id)inputAccessoryView 68 | { 69 | return nil; 70 | } 71 | @end 72 | 73 | @interface RCTWebViewBridge () 74 | 75 | @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart; 76 | @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish; 77 | @property (nonatomic, copy) RCTDirectEventBlock onLoadingError; 78 | @property (nonatomic, copy) RCTDirectEventBlock onShouldStartLoadWithRequest; 79 | @property (nonatomic, copy) RCTDirectEventBlock onBridgeMessage; 80 | 81 | @end 82 | 83 | @implementation RCTWebViewBridge 84 | { 85 | UIWebView *_webView; 86 | NSString *_injectedJavaScript; 87 | } 88 | 89 | - (instancetype)initWithFrame:(CGRect)frame 90 | { 91 | if ((self = [super initWithFrame:frame])) { 92 | super.backgroundColor = [UIColor clearColor]; 93 | _automaticallyAdjustContentInsets = YES; 94 | _contentInset = UIEdgeInsetsZero; 95 | _webView = [[UIWebView alloc] initWithFrame:self.bounds]; 96 | _webView.delegate = self; 97 | [self addSubview:_webView]; 98 | } 99 | return self; 100 | } 101 | 102 | RCT_NOT_IMPLEMENTED(- (instancetype)initWithCoder:(NSCoder *)aDecoder) 103 | 104 | - (void)goForward 105 | { 106 | [_webView goForward]; 107 | } 108 | 109 | - (void)goBack 110 | { 111 | [_webView goBack]; 112 | } 113 | 114 | - (void)reload 115 | { 116 | [_webView reload]; 117 | } 118 | 119 | - (void)sendToBridge:(NSString *)message 120 | { 121 | //we are warpping the send message in a function to make sure that if 122 | //WebView is not injected, we don't crash the app. 123 | NSString *format = NSStringMultiline( 124 | (function(){ 125 | if (WebViewBridge && WebViewBridge.__push__) { 126 | WebViewBridge.__push__('%@'); 127 | } 128 | }()); 129 | ); 130 | 131 | NSString *command = [NSString stringWithFormat: format, message]; 132 | [_webView stringByEvaluatingJavaScriptFromString:command]; 133 | } 134 | 135 | - (NSURL *)URL 136 | { 137 | return _webView.request.URL; 138 | } 139 | 140 | - (void)setSource:(NSDictionary *)source 141 | { 142 | if (![_source isEqualToDictionary:source]) { 143 | _source = [source copy]; 144 | 145 | // Check for a static html source first 146 | NSString *html = [RCTConvert NSString:source[@"html"]]; 147 | if (html) { 148 | NSURL *baseURL = [RCTConvert NSURL:source[@"baseUrl"]]; 149 | [_webView loadHTMLString:html baseURL:baseURL]; 150 | return; 151 | } 152 | 153 | NSURLRequest *request = [RCTConvert NSURLRequest:source]; 154 | // Because of the way React works, as pages redirect, we actually end up 155 | // passing the redirect urls back here, so we ignore them if trying to load 156 | // the same url. We'll expose a call to 'reload' to allow a user to load 157 | // the existing page. 158 | if ([request.URL isEqual:_webView.request.URL]) { 159 | return; 160 | } 161 | if (!request.URL) { 162 | // Clear the webview 163 | [_webView loadHTMLString:@"" baseURL:nil]; 164 | return; 165 | } 166 | [_webView loadRequest:request]; 167 | } 168 | } 169 | 170 | - (void)layoutSubviews 171 | { 172 | [super layoutSubviews]; 173 | _webView.frame = self.bounds; 174 | } 175 | 176 | - (void)setContentInset:(UIEdgeInsets)contentInset 177 | { 178 | _contentInset = contentInset; 179 | [RCTView autoAdjustInsetsForView:self 180 | withScrollView:_webView.scrollView 181 | updateOffset:NO]; 182 | } 183 | 184 | - (void)setBackgroundColor:(UIColor *)backgroundColor 185 | { 186 | CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor); 187 | self.opaque = _webView.opaque = (alpha == 1.0); 188 | _webView.backgroundColor = backgroundColor; 189 | } 190 | 191 | - (UIColor *)backgroundColor 192 | { 193 | return _webView.backgroundColor; 194 | } 195 | 196 | - (NSMutableDictionary *)baseEvent 197 | { 198 | NSMutableDictionary *event = [[NSMutableDictionary alloc] initWithDictionary:@{ 199 | @"url": _webView.request.URL.absoluteString ?: @"", 200 | @"loading" : @(_webView.loading), 201 | @"title": [_webView stringByEvaluatingJavaScriptFromString:@"document.title"], 202 | @"canGoBack": @(_webView.canGoBack), 203 | @"canGoForward" : @(_webView.canGoForward), 204 | }]; 205 | 206 | return event; 207 | } 208 | 209 | - (void)refreshContentInset 210 | { 211 | [RCTView autoAdjustInsetsForView:self 212 | withScrollView:_webView.scrollView 213 | updateOffset:YES]; 214 | } 215 | 216 | -(void)setHideKeyboardAccessoryView:(BOOL)hideKeyboardAccessoryView 217 | { 218 | if (!hideKeyboardAccessoryView) { 219 | return; 220 | } 221 | 222 | UIView* subview; 223 | for (UIView* view in _webView.scrollView.subviews) { 224 | if([[view.class description] hasPrefix:@"UIWeb"]) 225 | subview = view; 226 | } 227 | 228 | if(subview == nil) return; 229 | 230 | NSString* name = [NSString stringWithFormat:@"%@_SwizzleHelper", subview.class.superclass]; 231 | Class newClass = NSClassFromString(name); 232 | 233 | if(newClass == nil) 234 | { 235 | newClass = objc_allocateClassPair(subview.class, [name cStringUsingEncoding:NSASCIIStringEncoding], 0); 236 | if(!newClass) return; 237 | 238 | Method method = class_getInstanceMethod([_SwizzleHelper class], @selector(inputAccessoryView)); 239 | class_addMethod(newClass, @selector(inputAccessoryView), method_getImplementation(method), method_getTypeEncoding(method)); 240 | 241 | objc_registerClassPair(newClass); 242 | } 243 | 244 | object_setClass(subview, newClass); 245 | } 246 | 247 | #pragma mark - UIWebViewDelegate methods 248 | 249 | - (BOOL)webView:(__unused UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request 250 | navigationType:(UIWebViewNavigationType)navigationType 251 | { 252 | BOOL isJSNavigation = [request.URL.scheme isEqualToString:RCTJSNavigationScheme]; 253 | 254 | if (!isJSNavigation && [request.URL.scheme isEqualToString:RCTWebViewBridgeSchema]) { 255 | NSString* message = [webView stringByEvaluatingJavaScriptFromString:@"WebViewBridge.__fetch__()"]; 256 | 257 | NSMutableDictionary *onBridgeMessageEvent = [[NSMutableDictionary alloc] initWithDictionary:@{ 258 | @"messages": [self stringArrayJsonToArray: message] 259 | }]; 260 | 261 | _onBridgeMessage(onBridgeMessageEvent); 262 | 263 | isJSNavigation = YES; 264 | } 265 | 266 | // skip this for the JS Navigation handler 267 | if (!isJSNavigation && _onShouldStartLoadWithRequest) { 268 | NSMutableDictionary *event = [self baseEvent]; 269 | [event addEntriesFromDictionary: @{ 270 | @"url": (request.URL).absoluteString, 271 | @"navigationType": @(navigationType) 272 | }]; 273 | if (![self.delegate webView:self 274 | shouldStartLoadForRequest:event 275 | withCallback:_onShouldStartLoadWithRequest]) { 276 | return NO; 277 | } 278 | } 279 | 280 | if (_onLoadingStart) { 281 | // We have this check to filter out iframe requests and whatnot 282 | BOOL isTopFrame = [request.URL isEqual:request.mainDocumentURL]; 283 | if (isTopFrame) { 284 | NSMutableDictionary *event = [self baseEvent]; 285 | [event addEntriesFromDictionary: @{ 286 | @"url": (request.URL).absoluteString, 287 | @"navigationType": @(navigationType) 288 | }]; 289 | _onLoadingStart(event); 290 | } 291 | } 292 | 293 | // JS Navigation handler 294 | return !isJSNavigation; 295 | } 296 | 297 | - (void)webView:(__unused UIWebView *)webView didFailLoadWithError:(NSError *)error 298 | { 299 | if (_onLoadingError) { 300 | if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorCancelled) { 301 | // NSURLErrorCancelled is reported when a page has a redirect OR if you load 302 | // a new URL in the WebView before the previous one came back. We can just 303 | // ignore these since they aren't real errors. 304 | // http://stackoverflow.com/questions/1024748/how-do-i-fix-nsurlerrordomain-error-999-in-iphone-3-0-os 305 | return; 306 | } 307 | 308 | NSMutableDictionary *event = [self baseEvent]; 309 | [event addEntriesFromDictionary:@{ 310 | @"domain": error.domain, 311 | @"code": @(error.code), 312 | @"description": error.localizedDescription, 313 | }]; 314 | _onLoadingError(event); 315 | } 316 | } 317 | 318 | - (void)webViewDidFinishLoad:(UIWebView *)webView 319 | { 320 | //injecting WebViewBridge Script 321 | NSString *webViewBridgeScriptContent = [self webViewBridgeScript]; 322 | [webView stringByEvaluatingJavaScriptFromString:webViewBridgeScriptContent]; 323 | ////////////////////////////////////////////////////////////////////////////// 324 | 325 | if (_injectedJavaScript != nil) { 326 | NSString *jsEvaluationValue = [webView stringByEvaluatingJavaScriptFromString:_injectedJavaScript]; 327 | 328 | NSMutableDictionary *event = [self baseEvent]; 329 | event[@"jsEvaluationValue"] = jsEvaluationValue; 330 | 331 | _onLoadingFinish(event); 332 | } 333 | // we only need the final 'finishLoad' call so only fire the event when we're actually done loading. 334 | else if (_onLoadingFinish && !webView.loading && ![webView.request.URL.absoluteString isEqualToString:@"about:blank"]) { 335 | _onLoadingFinish([self baseEvent]); 336 | } 337 | } 338 | 339 | - (NSArray*)stringArrayJsonToArray:(NSString *)message 340 | { 341 | return [NSJSONSerialization JSONObjectWithData:[message dataUsingEncoding:NSUTF8StringEncoding] 342 | options:NSJSONReadingAllowFragments 343 | error:nil]; 344 | } 345 | 346 | //since there is no easy way to load the static lib resource in ios, 347 | //we are loading the script from this method. 348 | - (NSString *)webViewBridgeScript { 349 | // NSBundle *bundle = [NSBundle mainBundle]; 350 | // NSString *webViewBridgeScriptFile = [bundle pathForResource:@"webviewbridge" 351 | // ofType:@"js"]; 352 | // NSString *webViewBridgeScriptContent = [NSString stringWithContentsOfFile:webViewBridgeScriptFile 353 | // encoding:NSUTF8StringEncoding 354 | // error:nil]; 355 | 356 | return NSStringMultiline( 357 | (function (window) { 358 | 'use strict'; 359 | 360 | //Make sure that if WebViewBridge already in scope we don't override it. 361 | if (window.WebViewBridge) { 362 | return; 363 | } 364 | 365 | var RNWBSchema = 'wvb'; 366 | var sendQueue = []; 367 | var receiveQueue = []; 368 | var doc = window.document; 369 | var customEvent = doc.createEvent('Event'); 370 | 371 | function callFunc(func, message) { 372 | if ('function' === typeof func) { 373 | func(message); 374 | } 375 | } 376 | 377 | function signalNative() { 378 | window.location = RNWBSchema + '://message' + new Date().getTime(); 379 | } 380 | 381 | //I made the private function ugly signiture so user doesn't called them accidently. 382 | //if you do, then I have nothing to say. :( 383 | var WebViewBridge = { 384 | //this function will be called by native side to push a new message 385 | //to webview. 386 | __push__: function (message) { 387 | receiveQueue.push(message); 388 | //reason I need this setTmeout is to return this function as fast as 389 | //possible to release the native side thread. 390 | setTimeout(function () { 391 | var message = receiveQueue.pop(); 392 | callFunc(WebViewBridge.onMessage, message); 393 | }, 15); //this magic number is just a random small value. I don't like 0. 394 | }, 395 | __fetch__: function () { 396 | //since our sendQueue array only contains string, and our connection to native 397 | //can only accept string, we need to convert array of strings into single string. 398 | var messages = JSON.stringify(sendQueue); 399 | 400 | //we make sure that sendQueue is resets 401 | sendQueue = []; 402 | 403 | //return the messages back to native side. 404 | return messages; 405 | }, 406 | //make sure message is string. because only string can be sent to native, 407 | //if you don't pass it as string, onError function will be called. 408 | send: function (message) { 409 | if ('string' !== typeof message) { 410 | callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string"); 411 | return; 412 | } 413 | 414 | //we queue the messages to make sure that native can collects all of them in one shot. 415 | sendQueue.push(message); 416 | //signal the objective-c that there is a message in the queue 417 | signalNative(); 418 | }, 419 | onMessage: null, 420 | onError: null 421 | }; 422 | 423 | window.WebViewBridge = WebViewBridge; 424 | 425 | //dispatch event 426 | customEvent.initEvent('WebViewBridge', true, true); 427 | doc.dispatchEvent(customEvent); 428 | }(window)); 429 | ); 430 | } 431 | 432 | @end 433 | -------------------------------------------------------------------------------- /ios/RCTWebViewBridgeManager.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz) 6 | * All rights reserved 7 | * 8 | * This source code is licensed under the BSD-style license found in the 9 | * LICENSE file in the root directory of this source tree. An additional grant 10 | * of patent rights can be found in the PATENTS file in the same directory. 11 | */ 12 | 13 | #if __has_include() 14 | #import 15 | #else 16 | #import "RCTViewManager.h" 17 | #endif 18 | 19 | @interface RCTWebViewBridgeManager : RCTViewManager 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /ios/RCTWebViewBridgeManager.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * Copyright (c) 2015-present, Ali Najafizadeh (github.com/alinz) 6 | * All rights reserved 7 | * 8 | * This source code is licensed under the BSD-style license found in the 9 | * LICENSE file in the root directory of this source tree. An additional grant 10 | * of patent rights can be found in the PATENTS file in the same directory. 11 | */ 12 | 13 | #import "RCTWebViewBridgeManager.h" 14 | #import "RCTWebViewBridge.h" 15 | 16 | #if __has_include() 17 | #import 18 | #else 19 | #import "RCTBridge.h" 20 | #endif 21 | 22 | #if __has_include() 23 | #import 24 | #else 25 | #import "RCTUIManager.h" 26 | #endif 27 | 28 | #if __has_include() 29 | #import 30 | #else 31 | #import "UIView+React.h" 32 | #endif 33 | 34 | @interface RCTWebViewBridgeManager () 35 | 36 | @end 37 | 38 | @implementation RCTWebViewBridgeManager 39 | { 40 | NSConditionLock *_shouldStartLoadLock; 41 | BOOL _shouldStartLoad; 42 | } 43 | 44 | RCT_EXPORT_MODULE() 45 | 46 | - (UIView *)view 47 | { 48 | RCTWebViewBridge *webView = [RCTWebViewBridge new]; 49 | webView.delegate = self; 50 | return webView; 51 | } 52 | 53 | RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary) 54 | RCT_REMAP_VIEW_PROPERTY(bounces, _webView.scrollView.bounces, BOOL) 55 | RCT_REMAP_VIEW_PROPERTY(scrollEnabled, _webView.scrollView.scrollEnabled, BOOL) 56 | RCT_REMAP_VIEW_PROPERTY(scalesPageToFit, _webView.scalesPageToFit, BOOL) 57 | RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString) 58 | RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets) 59 | RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL) 60 | RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL) 61 | RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock) 62 | RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock) 63 | RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock) 64 | RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock) 65 | RCT_REMAP_VIEW_PROPERTY(allowsInlineMediaPlayback, _webView.allowsInlineMediaPlayback, BOOL) 66 | RCT_EXPORT_VIEW_PROPERTY(onBridgeMessage, RCTDirectEventBlock) 67 | RCT_REMAP_VIEW_PROPERTY(keyboardDisplayRequiresUserAction, _webView.keyboardDisplayRequiresUserAction, BOOL) 68 | 69 | - (NSDictionary *)constantsToExport 70 | { 71 | return @{ 72 | @"JSNavigationScheme": RCTJSNavigationScheme, 73 | @"NavigationType": @{ 74 | @"LinkClicked": @(UIWebViewNavigationTypeLinkClicked), 75 | @"FormSubmitted": @(UIWebViewNavigationTypeFormSubmitted), 76 | @"BackForward": @(UIWebViewNavigationTypeBackForward), 77 | @"Reload": @(UIWebViewNavigationTypeReload), 78 | @"FormResubmitted": @(UIWebViewNavigationTypeFormResubmitted), 79 | @"Other": @(UIWebViewNavigationTypeOther) 80 | }, 81 | }; 82 | } 83 | 84 | + (BOOL)requiresMainQueueSetup 85 | { 86 | return YES; 87 | } 88 | 89 | RCT_EXPORT_METHOD(goBack:(nonnull NSNumber *)reactTag) 90 | { 91 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { 92 | RCTWebViewBridge *view = viewRegistry[reactTag]; 93 | if (![view isKindOfClass:[RCTWebViewBridge class]]) { 94 | RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view); 95 | } else { 96 | [view goBack]; 97 | } 98 | }]; 99 | } 100 | 101 | RCT_EXPORT_METHOD(goForward:(nonnull NSNumber *)reactTag) 102 | { 103 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { 104 | RCTWebViewBridge* view = viewRegistry[reactTag]; 105 | if (![view isKindOfClass:[RCTWebViewBridge class]]) { 106 | RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view); 107 | } else { 108 | [view goForward]; 109 | } 110 | }]; 111 | } 112 | 113 | RCT_EXPORT_METHOD(reload:(nonnull NSNumber *)reactTag) 114 | { 115 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { 116 | RCTWebViewBridge *view = viewRegistry[reactTag]; 117 | if (![view isKindOfClass:[RCTWebViewBridge class]]) { 118 | RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view); 119 | } else { 120 | [view reload]; 121 | } 122 | }]; 123 | } 124 | 125 | RCT_EXPORT_METHOD(sendToBridge:(nonnull NSNumber *)reactTag 126 | value:(NSString*)message) 127 | { 128 | [self.bridge.uiManager addUIBlock:^(__unused RCTUIManager *uiManager, NSDictionary *viewRegistry) { 129 | RCTWebViewBridge *view = viewRegistry[reactTag]; 130 | if (![view isKindOfClass:[RCTWebViewBridge class]]) { 131 | RCTLogError(@"Invalid view returned from registry, expecting RCTWebViewBridge, got: %@", view); 132 | } else { 133 | [view sendToBridge: message]; 134 | } 135 | }]; 136 | } 137 | 138 | #pragma mark - Exported synchronous methods 139 | 140 | - (BOOL)webView:(__unused RCTWebViewBridge *)webView 141 | shouldStartLoadForRequest:(NSMutableDictionary *)request 142 | withCallback:(RCTDirectEventBlock)callback 143 | { 144 | _shouldStartLoadLock = [[NSConditionLock alloc] initWithCondition:arc4random()]; 145 | _shouldStartLoad = YES; 146 | request[@"lockIdentifier"] = @(_shouldStartLoadLock.condition); 147 | callback(request); 148 | 149 | // Block the main thread for a maximum of 250ms until the JS thread returns 150 | if ([_shouldStartLoadLock lockWhenCondition:0 beforeDate:[NSDate dateWithTimeIntervalSinceNow:.25]]) { 151 | BOOL returnValue = _shouldStartLoad; 152 | [_shouldStartLoadLock unlock]; 153 | _shouldStartLoadLock = nil; 154 | return returnValue; 155 | } else { 156 | RCTLogWarn(@"Did not receive response to shouldStartLoad in time, defaulting to YES"); 157 | return YES; 158 | } 159 | } 160 | 161 | RCT_EXPORT_METHOD(startLoadWithResult:(BOOL)result lockIdentifier:(NSInteger)lockIdentifier) 162 | { 163 | if ([_shouldStartLoadLock tryLockWhenCondition:lockIdentifier]) { 164 | _shouldStartLoad = result; 165 | [_shouldStartLoadLock unlockWithCondition:0]; 166 | } else { 167 | RCTLogWarn(@"startLoadWithResult invoked with invalid lockIdentifier: " 168 | "got %zd, expected %zd", lockIdentifier, _shouldStartLoadLock.condition); 169 | } 170 | } 171 | 172 | @end 173 | -------------------------------------------------------------------------------- /ios/React-Native-Webview-Bridge.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4114DC5C1C187CCB003CD988 /* RCTWebViewBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */; }; 11 | 4114DC5D1C187CCB003CD988 /* RCTWebViewBridgeManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 4114DC4A1C187C3A003CD988 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | runOnlyForDeploymentPostprocessing = 0; 23 | }; 24 | /* End PBXCopyFilesBuildPhase section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libReact-Native-Webview-Bridge.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 28 | 4114DC581C187CCB003CD988 /* RCTWebViewBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewBridge.h; sourceTree = SOURCE_ROOT; }; 29 | 4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewBridge.m; sourceTree = SOURCE_ROOT; }; 30 | 4114DC5A1C187CCB003CD988 /* RCTWebViewBridgeManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTWebViewBridgeManager.h; sourceTree = SOURCE_ROOT; }; 31 | 4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTWebViewBridgeManager.m; sourceTree = SOURCE_ROOT; }; 32 | 4114DC5F1C187CE4003CD988 /* webviewbridge.js */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.javascript; path = webviewbridge.js; sourceTree = ""; }; 33 | /* End PBXFileReference section */ 34 | 35 | /* Begin PBXFrameworksBuildPhase section */ 36 | 4114DC491C187C3A003CD988 /* Frameworks */ = { 37 | isa = PBXFrameworksBuildPhase; 38 | buildActionMask = 2147483647; 39 | files = ( 40 | ); 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXFrameworksBuildPhase section */ 44 | 45 | /* Begin PBXGroup section */ 46 | 4114DC431C187C3A003CD988 = { 47 | isa = PBXGroup; 48 | children = ( 49 | 4114DC4E1C187C3A003CD988 /* React-Native-Webview-Bridge */, 50 | 4114DC5E1C187CE4003CD988 /* scripts */, 51 | 4114DC4D1C187C3A003CD988 /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 4114DC4D1C187C3A003CD988 /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 4114DC4E1C187C3A003CD988 /* React-Native-Webview-Bridge */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 4114DC581C187CCB003CD988 /* RCTWebViewBridge.h */, 67 | 4114DC591C187CCB003CD988 /* RCTWebViewBridge.m */, 68 | 4114DC5A1C187CCB003CD988 /* RCTWebViewBridgeManager.h */, 69 | 4114DC5B1C187CCB003CD988 /* RCTWebViewBridgeManager.m */, 70 | ); 71 | path = "React-Native-Webview-Bridge"; 72 | sourceTree = ""; 73 | }; 74 | 4114DC5E1C187CE4003CD988 /* scripts */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 4114DC5F1C187CE4003CD988 /* webviewbridge.js */, 78 | ); 79 | name = scripts; 80 | path = ../scripts; 81 | sourceTree = ""; 82 | }; 83 | /* End PBXGroup section */ 84 | 85 | /* Begin PBXNativeTarget section */ 86 | 4114DC4B1C187C3A003CD988 /* React-Native-Webview-Bridge */ = { 87 | isa = PBXNativeTarget; 88 | buildConfigurationList = 4114DC551C187C3A003CD988 /* Build configuration list for PBXNativeTarget "React-Native-Webview-Bridge" */; 89 | buildPhases = ( 90 | 4114DC481C187C3A003CD988 /* Sources */, 91 | 4114DC491C187C3A003CD988 /* Frameworks */, 92 | 4114DC4A1C187C3A003CD988 /* CopyFiles */, 93 | ); 94 | buildRules = ( 95 | ); 96 | dependencies = ( 97 | ); 98 | name = "React-Native-Webview-Bridge"; 99 | productName = "React-Native-Webview-Bridge"; 100 | productReference = 4114DC4C1C187C3A003CD988 /* libReact-Native-Webview-Bridge.a */; 101 | productType = "com.apple.product-type.library.static"; 102 | }; 103 | /* End PBXNativeTarget section */ 104 | 105 | /* Begin PBXProject section */ 106 | 4114DC441C187C3A003CD988 /* Project object */ = { 107 | isa = PBXProject; 108 | attributes = { 109 | LastUpgradeCheck = 0710; 110 | ORGANIZATIONNAME = alinz; 111 | TargetAttributes = { 112 | 4114DC4B1C187C3A003CD988 = { 113 | CreatedOnToolsVersion = 7.1.1; 114 | }; 115 | }; 116 | }; 117 | buildConfigurationList = 4114DC471C187C3A003CD988 /* Build configuration list for PBXProject "React-Native-Webview-Bridge" */; 118 | compatibilityVersion = "Xcode 3.2"; 119 | developmentRegion = English; 120 | hasScannedForEncodings = 0; 121 | knownRegions = ( 122 | en, 123 | ); 124 | mainGroup = 4114DC431C187C3A003CD988; 125 | productRefGroup = 4114DC4D1C187C3A003CD988 /* Products */; 126 | projectDirPath = ""; 127 | projectRoot = ""; 128 | targets = ( 129 | 4114DC4B1C187C3A003CD988 /* React-Native-Webview-Bridge */, 130 | ); 131 | }; 132 | /* End PBXProject section */ 133 | 134 | /* Begin PBXSourcesBuildPhase section */ 135 | 4114DC481C187C3A003CD988 /* Sources */ = { 136 | isa = PBXSourcesBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | 4114DC5D1C187CCB003CD988 /* RCTWebViewBridgeManager.m in Sources */, 140 | 4114DC5C1C187CCB003CD988 /* RCTWebViewBridge.m in Sources */, 141 | ); 142 | runOnlyForDeploymentPostprocessing = 0; 143 | }; 144 | /* End PBXSourcesBuildPhase section */ 145 | 146 | /* Begin XCBuildConfiguration section */ 147 | 4114DC531C187C3A003CD988 /* Debug */ = { 148 | isa = XCBuildConfiguration; 149 | buildSettings = { 150 | ALWAYS_SEARCH_USER_PATHS = NO; 151 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 152 | CLANG_CXX_LIBRARY = "libc++"; 153 | CLANG_ENABLE_MODULES = YES; 154 | CLANG_ENABLE_OBJC_ARC = YES; 155 | CLANG_WARN_BOOL_CONVERSION = YES; 156 | CLANG_WARN_CONSTANT_CONVERSION = YES; 157 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 158 | CLANG_WARN_EMPTY_BODY = YES; 159 | CLANG_WARN_ENUM_CONVERSION = YES; 160 | CLANG_WARN_INT_CONVERSION = YES; 161 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 162 | CLANG_WARN_UNREACHABLE_CODE = YES; 163 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 164 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 165 | COPY_PHASE_STRIP = NO; 166 | DEBUG_INFORMATION_FORMAT = dwarf; 167 | ENABLE_STRICT_OBJC_MSGSEND = YES; 168 | ENABLE_TESTABILITY = YES; 169 | GCC_C_LANGUAGE_STANDARD = gnu99; 170 | GCC_DYNAMIC_NO_PIC = NO; 171 | GCC_NO_COMMON_BLOCKS = YES; 172 | GCC_OPTIMIZATION_LEVEL = 0; 173 | GCC_PREPROCESSOR_DEFINITIONS = ( 174 | "DEBUG=1", 175 | "$(inherited)", 176 | ); 177 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 178 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 179 | GCC_WARN_UNDECLARED_SELECTOR = YES; 180 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 181 | GCC_WARN_UNUSED_FUNCTION = YES; 182 | GCC_WARN_UNUSED_VARIABLE = YES; 183 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 184 | MTL_ENABLE_DEBUG_INFO = YES; 185 | ONLY_ACTIVE_ARCH = YES; 186 | SDKROOT = iphoneos; 187 | }; 188 | name = Debug; 189 | }; 190 | 4114DC541C187C3A003CD988 /* Release */ = { 191 | isa = XCBuildConfiguration; 192 | buildSettings = { 193 | ALWAYS_SEARCH_USER_PATHS = NO; 194 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 195 | CLANG_CXX_LIBRARY = "libc++"; 196 | CLANG_ENABLE_MODULES = YES; 197 | CLANG_ENABLE_OBJC_ARC = YES; 198 | CLANG_WARN_BOOL_CONVERSION = YES; 199 | CLANG_WARN_CONSTANT_CONVERSION = YES; 200 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 201 | CLANG_WARN_EMPTY_BODY = YES; 202 | CLANG_WARN_ENUM_CONVERSION = YES; 203 | CLANG_WARN_INT_CONVERSION = YES; 204 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 205 | CLANG_WARN_UNREACHABLE_CODE = YES; 206 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 207 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 208 | COPY_PHASE_STRIP = NO; 209 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 210 | ENABLE_NS_ASSERTIONS = NO; 211 | ENABLE_STRICT_OBJC_MSGSEND = YES; 212 | GCC_C_LANGUAGE_STANDARD = gnu99; 213 | GCC_NO_COMMON_BLOCKS = YES; 214 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 215 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 216 | GCC_WARN_UNDECLARED_SELECTOR = YES; 217 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 218 | GCC_WARN_UNUSED_FUNCTION = YES; 219 | GCC_WARN_UNUSED_VARIABLE = YES; 220 | IPHONEOS_DEPLOYMENT_TARGET = 9.1; 221 | MTL_ENABLE_DEBUG_INFO = NO; 222 | SDKROOT = iphoneos; 223 | VALIDATE_PRODUCT = YES; 224 | }; 225 | name = Release; 226 | }; 227 | 4114DC561C187C3A003CD988 /* Debug */ = { 228 | isa = XCBuildConfiguration; 229 | buildSettings = { 230 | HEADER_SEARCH_PATHS = ( 231 | "$(inhereted)", 232 | "$(SRCROOT)/../../../node_module/React/**", 233 | "$(SRCROOT)/../../../node_modules/react-native/React/**", 234 | ); 235 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 236 | OTHER_LDFLAGS = "-ObjC"; 237 | PRODUCT_NAME = "$(TARGET_NAME)"; 238 | SKIP_INSTALL = YES; 239 | }; 240 | name = Debug; 241 | }; 242 | 4114DC571C187C3A003CD988 /* Release */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | HEADER_SEARCH_PATHS = ( 246 | "$(inhereted)", 247 | "$(SRCROOT)/../../../node_module/React/**", 248 | "$(SRCROOT)/../../../node_modules/react-native/React/**", 249 | ); 250 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 251 | OTHER_LDFLAGS = "-ObjC"; 252 | PRODUCT_NAME = "$(TARGET_NAME)"; 253 | SKIP_INSTALL = YES; 254 | }; 255 | name = Release; 256 | }; 257 | /* End XCBuildConfiguration section */ 258 | 259 | /* Begin XCConfigurationList section */ 260 | 4114DC471C187C3A003CD988 /* Build configuration list for PBXProject "React-Native-Webview-Bridge" */ = { 261 | isa = XCConfigurationList; 262 | buildConfigurations = ( 263 | 4114DC531C187C3A003CD988 /* Debug */, 264 | 4114DC541C187C3A003CD988 /* Release */, 265 | ); 266 | defaultConfigurationIsVisible = 0; 267 | defaultConfigurationName = Release; 268 | }; 269 | 4114DC551C187C3A003CD988 /* Build configuration list for PBXNativeTarget "React-Native-Webview-Bridge" */ = { 270 | isa = XCConfigurationList; 271 | buildConfigurations = ( 272 | 4114DC561C187C3A003CD988 /* Debug */, 273 | 4114DC571C187C3A003CD988 /* Release */, 274 | ); 275 | defaultConfigurationIsVisible = 0; 276 | defaultConfigurationName = Release; 277 | }; 278 | /* End XCConfigurationList section */ 279 | }; 280 | rootObject = 4114DC441C187C3A003CD988 /* Project object */; 281 | } 282 | -------------------------------------------------------------------------------- /ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wix-playground/react-native-webview-bridge/ecbd800a1fd9df2de2bd84ec0ed7835e3e2164de/ios/React-Native-Webview-Bridge.xcodeproj/project.xcworkspace/xcuserdata/ali.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/react-native-webview-bridge.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ios/React-Native-Webview-Bridge.xcodeproj/xcuserdata/ali.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | React-Native-Webview-Bridge.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 4114DC4B1C187C3A003CD988 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-webview-bridge-updated", 3 | "version": "1.1.1", 4 | "description": "React Native WebView Javascript Bridge - updated", 5 | "license": "MIT", 6 | "author": "Ali Najafizadeh", 7 | "contributors": [ 8 | "Daniel Zlotin ", 9 | "Yedidya Kennard " 10 | ], 11 | "directories": { 12 | "example": "examples", 13 | "lib": "webview-bridge" 14 | }, 15 | "keywords": [ 16 | "react-native", 17 | "react-component", 18 | "ios", 19 | "webview-bridge" 20 | ], 21 | "homepage": "https://github.com/wix/react-native-webview-bridge", 22 | "bugs": { 23 | "url": "https://github.com/wix/react-native-webview-bridge/issues" 24 | }, 25 | "repository": { 26 | "type": "git", 27 | "url": "git@github.com:wix/react-native-webview-bridge.git" 28 | }, 29 | "scripts": { 30 | "build": ":", 31 | "test": ":", 32 | "release": "npm version patch && npm publish && git push" 33 | }, 34 | "main": "webview-bridge", 35 | "dependencies": { 36 | "invariant": "2.2.0", 37 | "keymirror": "0.1.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /scripts/webviewbridge.js: -------------------------------------------------------------------------------- 1 | (function (window) { 2 | 'use strict'; 3 | 4 | //Make sure that if WebViewBridge already in scope we don't override it. 5 | if (window.WebViewBridge) { 6 | return; 7 | } 8 | 9 | var RNWBSchema = 'wvb'; 10 | var sendQueue = []; 11 | var receiveQueue = []; 12 | var doc = window.document; 13 | var customEvent = doc.createEvent('Event'); 14 | 15 | function callFunc(func, message) { 16 | if ('function' === typeof func) { 17 | func(message); 18 | } 19 | } 20 | 21 | function signalNative() { 22 | window.location = RNWBSchema + '://message' + new Date().getTime(); 23 | } 24 | 25 | //I made the private function ugly signiture so user doesn't called them accidently. 26 | //if you do, then I have nothing to say. :( 27 | var WebViewBridge = { 28 | //this function will be called by native side to push a new message 29 | //to webview. 30 | __push__: function (message) { 31 | receiveQueue.push(message); 32 | //reason I need this setTmeout is to return this function as fast as 33 | //possible to release the native side thread. 34 | setTimeout(function () { 35 | var message = receiveQueue.pop(); 36 | callFunc(WebViewBridge.onMessage, message); 37 | }, 15); //this magic number is just a random small value. I don't like 0. 38 | }, 39 | __fetch__: function () { 40 | //since our sendQueue array only contains string, and our connection to native 41 | //can only accept string, we need to convert array of strings into single string. 42 | var messages = JSON.stringify(sendQueue); 43 | 44 | //we make sure that sendQueue is resets 45 | sendQueue = []; 46 | 47 | //return the messages back to native side. 48 | return messages; 49 | }, 50 | //make sure message is string. because only string can be sent to native, 51 | //if you don't pass it as string, onError function will be called. 52 | send: function (message) { 53 | if ('string' !== typeof message) { 54 | callFunc(WebViewBridge.onError, "message is type '" + typeof message + "', and it needs to be string"); 55 | return; 56 | } 57 | 58 | //we queue the messages to make sure that native can collects all of them in one shot. 59 | sendQueue.push(message); 60 | //signal the objective-c that there is a message in the queue 61 | signalNative(); 62 | }, 63 | onMessage: null, 64 | onError: null 65 | }; 66 | 67 | window.WebViewBridge = WebViewBridge; 68 | 69 | //dispatch event 70 | customEvent.initEvent('WebViewBridge', true, true); 71 | doc.dispatchEvent(customEvent); 72 | }(window)); 73 | -------------------------------------------------------------------------------- /webview-bridge/index.android.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * Copyright (c) 2016-present, Ali Najafizadeh 6 | * All rights reserved. 7 | * 8 | * This source code is licensed under the BSD-style license found in the 9 | * LICENSE file in the root directory of this source tree. An additional grant 10 | * of patent rights can be found in the PATENTS file in the same directory. 11 | * 12 | * @providesModule WebViewBridge 13 | */ 14 | 'use strict'; 15 | 16 | var React = require('react'); 17 | var PropTypes = require('prop-types'); 18 | var createReactClass = require('create-react-class'); 19 | var ReactNative = require('react-native'); 20 | var invariant = require('invariant'); 21 | var keyMirror = require('keymirror'); 22 | var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); 23 | 24 | var { 25 | ReactNativeViewAttributes, 26 | UIManager, 27 | EdgeInsetsPropType, 28 | StyleSheet, 29 | Text, 30 | View, 31 | WebView, 32 | requireNativeComponent, 33 | DeviceEventEmitter, 34 | NativeModules: { 35 | WebViewBridgeManager 36 | } 37 | } = ReactNative; 38 | 39 | var RCT_WEBVIEWBRIDGE_REF = 'webviewbridge'; 40 | 41 | var WebViewBridgeState = keyMirror({ 42 | IDLE: null, 43 | LOADING: null, 44 | ERROR: null, 45 | }); 46 | 47 | var RCTWebViewBridge = requireNativeComponent('RCTWebViewBridge', WebViewBridge); 48 | 49 | /** 50 | * Renders a native WebView. 51 | */ 52 | var WebViewBridge = createReactClass({ 53 | 54 | propTypes: { 55 | ...RCTWebViewBridge.propTypes, 56 | 57 | /** 58 | * Will be called once the message is being sent from webview 59 | */ 60 | onBridgeMessage: PropTypes.func, 61 | }, 62 | 63 | getInitialState: function() { 64 | return { 65 | viewState: WebViewBridgeState.IDLE, 66 | lastErrorEvent: null, 67 | startInLoadingState: true, 68 | }; 69 | }, 70 | 71 | 72 | componentWillMount: function() { 73 | DeviceEventEmitter.addListener("webViewBridgeMessage", (body) => { 74 | const { onBridgeMessage } = this.props; 75 | const message = body.message; 76 | if (onBridgeMessage) { 77 | onBridgeMessage(message); 78 | } 79 | }); 80 | 81 | if (this.props.startInLoadingState) { 82 | this.setState({viewState: WebViewBridgeState.LOADING}); 83 | } 84 | }, 85 | 86 | render: function() { 87 | var otherView = null; 88 | 89 | if (this.state.viewState === WebViewBridgeState.LOADING) { 90 | otherView = this.props.renderLoading && this.props.renderLoading(); 91 | } else if (this.state.viewState === WebViewBridgeState.ERROR) { 92 | var errorEvent = this.state.lastErrorEvent; 93 | otherView = this.props.renderError && this.props.renderError( 94 | errorEvent.domain, 95 | errorEvent.code, 96 | errorEvent.description); 97 | } else if (this.state.viewState !== WebViewBridgeState.IDLE) { 98 | console.error('RCTWebViewBridge invalid state encountered: ' + this.state.loading); 99 | } 100 | 101 | var webViewStyles = [styles.container, this.props.style]; 102 | if (this.state.viewState === WebViewBridgeState.LOADING || 103 | this.state.viewState === WebViewBridgeState.ERROR) { 104 | // if we're in either LOADING or ERROR states, don't show the webView 105 | webViewStyles.push(styles.hidden); 106 | } 107 | 108 | var {javaScriptEnabled, domStorageEnabled} = this.props; 109 | if (this.props.javaScriptEnabledAndroid) { 110 | console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); 111 | javaScriptEnabled = this.props.javaScriptEnabledAndroid; 112 | } 113 | if (this.props.domStorageEnabledAndroid) { 114 | console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); 115 | domStorageEnabled = this.props.domStorageEnabledAndroid; 116 | } 117 | 118 | let {source, ...props} = {...this.props}; 119 | 120 | var webView = 121 | ; 133 | 134 | return ( 135 | 136 | {webView} 137 | {otherView} 138 | 139 | ); 140 | }, 141 | 142 | onMessage(event) { 143 | if (this.props.onBridgeMessage != null && event.nativeEvent != null) { 144 | this.props.onBridgeMessage(event.nativeEvent.message) 145 | } 146 | }, 147 | 148 | goForward: function() { 149 | UIManager.dispatchViewManagerCommand( 150 | this.getWebViewBridgeHandle(), 151 | UIManager.RCTWebViewBridge.Commands.goForward, 152 | null 153 | ); 154 | }, 155 | 156 | goBack: function() { 157 | UIManager.dispatchViewManagerCommand( 158 | this.getWebViewBridgeHandle(), 159 | UIManager.RCTWebViewBridge.Commands.goBack, 160 | null 161 | ); 162 | }, 163 | 164 | reload: function() { 165 | UIManager.dispatchViewManagerCommand( 166 | this.getWebViewBridgeHandle(), 167 | UIManager.RCTWebViewBridge.Commands.reload, 168 | null 169 | ); 170 | }, 171 | 172 | sendToBridge: function (message: string) { 173 | UIManager.dispatchViewManagerCommand( 174 | this.getWebViewBridgeHandle(), 175 | UIManager.RCTWebViewBridge.Commands.sendToBridge, 176 | [message] 177 | ); 178 | }, 179 | 180 | /** 181 | * We return an event with a bunch of fields including: 182 | * url, title, loading, canGoBack, canGoForward 183 | */ 184 | updateNavigationState: function(event) { 185 | if (this.props.onNavigationStateChange) { 186 | this.props.onNavigationStateChange(event.nativeEvent); 187 | } 188 | }, 189 | 190 | getWebViewBridgeHandle: function() { 191 | return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEWBRIDGE_REF]); 192 | }, 193 | 194 | onLoadingStart: function(event) { 195 | var onLoadStart = this.props.onLoadStart; 196 | onLoadStart && onLoadStart(event); 197 | this.updateNavigationState(event); 198 | }, 199 | 200 | onLoadingError: function(event) { 201 | event.persist(); // persist this event because we need to store it 202 | var {onError, onLoadEnd} = this.props; 203 | onError && onError(event); 204 | onLoadEnd && onLoadEnd(event); 205 | 206 | this.setState({ 207 | lastErrorEvent: event.nativeEvent, 208 | viewState: WebViewBridgeState.ERROR 209 | }); 210 | }, 211 | 212 | onLoadingFinish: function(event) { 213 | var {onLoad, onLoadEnd} = this.props; 214 | onLoad && onLoad(event); 215 | onLoadEnd && onLoadEnd(event); 216 | this.setState({ 217 | viewState: WebViewBridgeState.IDLE, 218 | }); 219 | this.updateNavigationState(event); 220 | }, 221 | }); 222 | 223 | 224 | var styles = StyleSheet.create({ 225 | container: { 226 | flex: 1, 227 | }, 228 | hidden: { 229 | height: 0, 230 | flex: 0, // disable 'flex:1' when hiding a View 231 | }, 232 | }); 233 | 234 | module.exports = WebViewBridge; -------------------------------------------------------------------------------- /webview-bridge/index.ios.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * Copyright (c) 2016-present, Ali Najafizadeh 6 | * All rights reserved. 7 | * 8 | * This source code is licensed under the BSD-style license found in the 9 | * LICENSE file in the root directory of this source tree. An additional grant 10 | * of patent rights can be found in the PATENTS file in the same directory. 11 | * 12 | * @providesModule WebViewBridge 13 | * @flow 14 | */ 15 | 'use strict'; 16 | 17 | 18 | var React = require('react'); 19 | var PropTypes = require('prop-types'); 20 | var createReactClass = require('create-react-class'); 21 | var ReactNative = require('react-native'); 22 | var invariant = require('invariant'); 23 | var keyMirror = require('keymirror'); 24 | var resolveAssetSource = require('react-native/Libraries/Image/resolveAssetSource'); 25 | 26 | var { 27 | ActivityIndicator, 28 | EdgeInsetsPropType, 29 | StyleSheet, 30 | Text, 31 | View, 32 | WebView, 33 | requireNativeComponent, 34 | UIManager, 35 | NativeModules: { 36 | WebViewBridgeManager 37 | } 38 | } = ReactNative; 39 | 40 | var BGWASH = 'rgba(255,255,255,0.8)'; 41 | var RCT_WEBVIEWBRIDGE_REF = 'webviewbridge'; 42 | 43 | var RCTWebViewBridgeManager = WebViewBridgeManager; 44 | 45 | var WebViewBridgeState = keyMirror({ 46 | IDLE: null, 47 | LOADING: null, 48 | ERROR: null, 49 | }); 50 | 51 | var NavigationType = { 52 | click: RCTWebViewBridgeManager.NavigationType.LinkClicked, 53 | formsubmit: RCTWebViewBridgeManager.NavigationType.FormSubmitted, 54 | backforward: RCTWebViewBridgeManager.NavigationType.BackForward, 55 | reload: RCTWebViewBridgeManager.NavigationType.Reload, 56 | formresubmit: RCTWebViewBridgeManager.NavigationType.FormResubmitted, 57 | other: RCTWebViewBridgeManager.NavigationType.Other, 58 | }; 59 | 60 | var JSNavigationScheme = RCTWebViewBridgeManager.JSNavigationScheme; 61 | 62 | type ErrorEvent = { 63 | domain: any; 64 | code: any; 65 | description: any; 66 | } 67 | 68 | type Event = Object; 69 | 70 | var defaultRenderLoading = () => ( 71 | 72 | 73 | 74 | ); 75 | var defaultRenderError = (errorDomain, errorCode, errorDesc) => ( 76 | 77 | 78 | Error loading page 79 | 80 | 81 | {'Domain: ' + errorDomain} 82 | 83 | 84 | {'Error Code: ' + errorCode} 85 | 86 | 87 | {'Description: ' + errorDesc} 88 | 89 | 90 | ); 91 | 92 | /** 93 | * Renders a native WebView. 94 | */ 95 | var WebViewBridge = createReactClass({ 96 | statics: { 97 | JSNavigationScheme: JSNavigationScheme, 98 | NavigationType: NavigationType, 99 | }, 100 | 101 | propTypes: { 102 | ...WebView.propTypes, 103 | 104 | /** 105 | * Will be called once the message is being sent from webview 106 | */ 107 | onBridgeMessage: PropTypes.func, 108 | 109 | hideKeyboardAccessoryView: PropTypes.bool, 110 | 111 | keyboardDisplayRequiresUserAction: PropTypes.bool, 112 | }, 113 | 114 | getInitialState: function() { 115 | return { 116 | viewState: WebViewBridgeState.IDLE, 117 | lastErrorEvent: (null: ?ErrorEvent), 118 | startInLoadingState: true, 119 | }; 120 | }, 121 | 122 | componentWillMount: function() { 123 | if (this.props.startInLoadingState) { 124 | this.setState({viewState: WebViewBridgeState.LOADING}); 125 | } 126 | }, 127 | 128 | render: function() { 129 | var otherView = null; 130 | 131 | if (this.state.viewState === WebViewBridgeState.LOADING) { 132 | otherView = (this.props.renderLoading || defaultRenderLoading)(); 133 | } else if (this.state.viewState === WebViewBridgeState.ERROR) { 134 | var errorEvent = this.state.lastErrorEvent; 135 | invariant( 136 | errorEvent != null, 137 | 'lastErrorEvent expected to be non-null' 138 | ); 139 | otherView = (this.props.renderError || defaultRenderError)( 140 | errorEvent.domain, 141 | errorEvent.code, 142 | errorEvent.description 143 | ); 144 | } else if (this.state.viewState !== WebViewBridgeState.IDLE) { 145 | console.error( 146 | 'RCTWebViewBridge invalid state encountered: ' + this.state.loading 147 | ); 148 | } 149 | 150 | var webViewStyles = [styles.container, styles.webView, this.props.style]; 151 | if (this.state.viewState === WebViewBridgeState.LOADING || 152 | this.state.viewState === WebViewBridgeState.ERROR) { 153 | // if we're in either LOADING or ERROR states, don't show the webView 154 | webViewStyles.push(styles.hidden); 155 | } 156 | 157 | var onShouldStartLoadWithRequest = this.props.onShouldStartLoadWithRequest && ((event: Event) => { 158 | var shouldStart = this.props.onShouldStartLoadWithRequest && 159 | this.props.onShouldStartLoadWithRequest(event.nativeEvent); 160 | RCTWebViewBridgeManager.startLoadWithResult(!!shouldStart, event.nativeEvent.lockIdentifier); 161 | }); 162 | 163 | var {javaScriptEnabled, domStorageEnabled} = this.props; 164 | if (this.props.javaScriptEnabledAndroid) { 165 | console.warn('javaScriptEnabledAndroid is deprecated. Use javaScriptEnabled instead'); 166 | javaScriptEnabled = this.props.javaScriptEnabledAndroid; 167 | } 168 | if (this.props.domStorageEnabledAndroid) { 169 | console.warn('domStorageEnabledAndroid is deprecated. Use domStorageEnabled instead'); 170 | domStorageEnabled = this.props.domStorageEnabledAndroid; 171 | } 172 | 173 | var onBridgeMessage = (event: Event) => { 174 | const onBridgeMessageCallback = this.props.onBridgeMessage; 175 | if (onBridgeMessageCallback) { 176 | const messages = event.nativeEvent.messages; 177 | messages.forEach((message) => { 178 | onBridgeMessageCallback(message); 179 | }); 180 | } 181 | }; 182 | 183 | let {source, ...props} = {...this.props}; 184 | delete props.onBridgeMessage; 185 | delete props.onShouldStartLoadWithRequest; 186 | 187 | var webView = 188 | ; 200 | 201 | return ( 202 | 203 | {webView} 204 | {otherView} 205 | 206 | ); 207 | }, 208 | 209 | goForward: function() { 210 | UIManager.dispatchViewManagerCommand( 211 | this.getWebViewBridgeHandle(), 212 | UIManager.RCTWebViewBridge.Commands.goForward, 213 | null 214 | ); 215 | }, 216 | 217 | goBack: function() { 218 | UIManager.dispatchViewManagerCommand( 219 | this.getWebViewBridgeHandle(), 220 | UIManager.RCTWebViewBridge.Commands.goBack, 221 | null 222 | ); 223 | }, 224 | 225 | reload: function() { 226 | UIManager.dispatchViewManagerCommand( 227 | this.getWebViewBridgeHandle(), 228 | UIManager.RCTWebViewBridge.Commands.reload, 229 | null 230 | ); 231 | }, 232 | 233 | sendToBridge: function (message: string) { 234 | WebViewBridgeManager.sendToBridge(this.getWebViewBridgeHandle(), message); 235 | }, 236 | 237 | /** 238 | * We return an event with a bunch of fields including: 239 | * url, title, loading, canGoBack, canGoForward 240 | */ 241 | updateNavigationState: function(event: Event) { 242 | if (this.props.onNavigationStateChange) { 243 | this.props.onNavigationStateChange(event.nativeEvent); 244 | } 245 | }, 246 | 247 | getWebViewBridgeHandle: function(): any { 248 | return ReactNative.findNodeHandle(this.refs[RCT_WEBVIEWBRIDGE_REF]); 249 | }, 250 | 251 | onLoadingStart: function(event: Event) { 252 | var onLoadStart = this.props.onLoadStart; 253 | onLoadStart && onLoadStart(event); 254 | this.updateNavigationState(event); 255 | }, 256 | 257 | onLoadingError: function(event: Event) { 258 | event.persist(); // persist this event because we need to store it 259 | var {onError, onLoadEnd} = this.props; 260 | onError && onError(event); 261 | onLoadEnd && onLoadEnd(event); 262 | console.warn('Encountered an error loading page', event.nativeEvent); 263 | 264 | this.setState({ 265 | lastErrorEvent: event.nativeEvent, 266 | viewState: WebViewBridgeState.ERROR 267 | }); 268 | }, 269 | 270 | onLoadingFinish: function(event: Event) { 271 | var {onLoad, onLoadEnd} = this.props; 272 | onLoad && onLoad(event); 273 | onLoadEnd && onLoadEnd(event); 274 | this.setState({ 275 | viewState: WebViewBridgeState.IDLE, 276 | }); 277 | this.updateNavigationState(event); 278 | }, 279 | }); 280 | 281 | var RCTWebViewBridge = requireNativeComponent('RCTWebViewBridge', WebViewBridge, { 282 | nativeOnly: { 283 | onLoadingStart: true, 284 | onLoadingError: true, 285 | onLoadingFinish: true, 286 | }, 287 | }); 288 | 289 | var styles = StyleSheet.create({ 290 | container: { 291 | flex: 1, 292 | }, 293 | errorContainer: { 294 | flex: 1, 295 | justifyContent: 'center', 296 | alignItems: 'center', 297 | backgroundColor: BGWASH, 298 | }, 299 | errorText: { 300 | fontSize: 14, 301 | textAlign: 'center', 302 | marginBottom: 2, 303 | }, 304 | errorTextTitle: { 305 | fontSize: 15, 306 | fontWeight: '500', 307 | marginBottom: 10, 308 | }, 309 | hidden: { 310 | height: 0, 311 | flex: 0, // disable 'flex:1' when hiding a View 312 | }, 313 | loadingView: { 314 | backgroundColor: BGWASH, 315 | flex: 1, 316 | justifyContent: 'center', 317 | alignItems: 'center', 318 | }, 319 | webView: { 320 | backgroundColor: '#ffffff', 321 | } 322 | }); 323 | 324 | module.exports = WebViewBridge; 325 | --------------------------------------------------------------------------------