瀏覽代碼

merge master

Kesha Antonov 4 年之前
父節點
當前提交
03870d4cb9
共有 100 個檔案被更改,包括 5893 行新增503 行删除
  1. 1
    1
      .circleci/config.yml
  2. 3
    3
      .github/workflows/detox.yml
  3. 108
    0
      .github/workflows/scripts/install-vs-features.ps1
  4. 67
    0
      .github/workflows/windows-ci.yml
  5. 6
    1
      .gitignore
  6. 15
    4
      README.md
  7. 31
    0
      __tests__/Alert.test.js
  8. 24
    15
      android/build.gradle
  9. 3
    3
      android/gradle.properties
  10. 201
    32
      android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java
  11. 174
    75
      android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java
  12. 0
    27
      android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java
  13. 15
    0
      android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.kt
  14. 0
    0
      apple/RNCWKProcessPoolManager.h
  15. 0
    0
      apple/RNCWKProcessPoolManager.m
  16. 11
    0
      apple/RNCWebView.h
  17. 527
    168
      apple/RNCWebView.m
  18. 0
    0
      apple/RNCWebViewManager.h
  19. 12
    13
      apple/RNCWebViewManager.m
  20. 1
    0
      babel.config.js
  21. 1
    1
      bin/setup
  22. 46
    0
      docs/Contributing.md
  23. 14
    14
      docs/Custom-Android.md
  24. 45
    11
      docs/Getting-Started.md
  25. 202
    47
      docs/Guide.md
  26. 104
    0
      docs/README.portuguese.md
  27. 308
    88
      docs/Reference.md
  28. 1
    0
      example/.gitattributes
  29. 67
    0
      example/.gitignore
  30. 6
    0
      example/.prettierrc.js
  31. 1
    0
      example/.watchmanconfig
  32. 216
    0
      example/App.tsx
  33. 55
    0
      example/android/app/_BUCK
  34. 204
    0
      example/android/app/build.gradle
  35. 19
    0
      example/android/app/build_defs.bzl
  36. 二進制
      example/android/app/debug.keystore
  37. 10
    0
      example/android/app/proguard-rules.pro
  38. 8
    0
      example/android/app/src/debug/AndroidManifest.xml
  39. 27
    0
      example/android/app/src/main/AndroidManifest.xml
  40. 15
    0
      example/android/app/src/main/java/com/example/MainActivity.java
  41. 76
    0
      example/android/app/src/main/java/com/example/MainApplication.java
  42. 二進制
      example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
  43. 二進制
      example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
  44. 二進制
      example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
  45. 二進制
      example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
  46. 二進制
      example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
  47. 二進制
      example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
  48. 二進制
      example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  49. 二進制
      example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
  50. 二進制
      example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  51. 二進制
      example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
  52. 3
    0
      example/android/app/src/main/res/values/strings.xml
  53. 9
    0
      example/android/app/src/main/res/values/styles.xml
  54. 38
    0
      example/android/build.gradle
  55. 21
    0
      example/android/gradle.properties
  56. 二進制
      example/android/gradle/wrapper/gradle-wrapper.jar
  57. 5
    0
      example/android/gradle/wrapper/gradle-wrapper.properties
  58. 188
    0
      example/android/gradlew
  59. 100
    0
      example/android/gradlew.bat
  60. 5
    0
      example/android/settings.gradle
  61. 4
    0
      example/app.json
  62. 9
    0
      example/assets/test.html
  63. 3
    0
      example/babel.config.js
  64. 72
    0
      example/examples/Alerts.tsx
  65. 54
    0
      example/examples/Background.tsx
  66. 55
    0
      example/examples/Downloads.tsx
  67. 160
    0
      example/examples/Injection.tsx
  68. 16
    0
      example/examples/LocalPageLoad.tsx
  69. 68
    0
      example/examples/Scrolling.tsx
  70. 69
    0
      example/examples/Uploads.tsx
  71. 9
    0
      example/index.js
  72. 56
    0
      example/ios/Podfile
  73. 368
    0
      example/ios/Podfile.lock
  74. 53
    0
      example/ios/example-tvOS/Info.plist
  75. 24
    0
      example/ios/example-tvOSTests/Info.plist
  76. 929
    0
      example/ios/example.xcodeproj/project.pbxproj
  77. 88
    0
      example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme
  78. 88
    0
      example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme
  79. 10
    0
      example/ios/example.xcworkspace/contents.xcworkspacedata
  80. 8
    0
      example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  81. 15
    0
      example/ios/example/AppDelegate.h
  82. 42
    0
      example/ios/example/AppDelegate.m
  83. 42
    0
      example/ios/example/Base.lproj/LaunchScreen.xib
  84. 38
    0
      example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json
  85. 6
    0
      example/ios/example/Images.xcassets/Contents.json
  86. 57
    0
      example/ios/example/Info.plist
  87. 16
    0
      example/ios/example/main.m
  88. 24
    0
      example/ios/exampleTests/Info.plist
  89. 72
    0
      example/ios/exampleTests/exampleTests.m
  90. 42
    0
      example/macos/Podfile
  91. 199
    0
      example/macos/Podfile.lock
  92. 8
    0
      example/macos/example-iOS/AppDelegate.h
  93. 35
    0
      example/macos/example-iOS/AppDelegate.m
  94. 42
    0
      example/macos/example-iOS/Base.lproj/LaunchScreen.xib
  95. 38
    0
      example/macos/example-iOS/Images.xcassets/AppIcon.appiconset/Contents.json
  96. 6
    0
      example/macos/example-iOS/Images.xcassets/Contents.json
  97. 57
    0
      example/macos/example-iOS/Info.plist
  98. 9
    0
      example/macos/example-iOS/main.m
  99. 9
    0
      example/macos/example-macOS/AppDelegate.h
  100. 0
    0
      example/macos/example-macOS/AppDelegate.m

+ 1
- 1
.circleci/config.yml 查看文件

@@ -34,7 +34,7 @@ jobs:
34 34
             - node_modules-{{ arch }}-{{ checksum "yarn.lock" }}
35 35
 
36 36
       - run:
37
-          name: Run Tests
37
+          name: Lint checks
38 38
           command: yarn ci
39 39
 
40 40
   publish:

+ 3
- 3
.github/workflows/detox.yml 查看文件

@@ -1,5 +1,5 @@
1 1
 name: 'Detox CI Tests'
2
-on: [push]
2
+on: [pull_request]
3 3
 
4 4
 jobs:
5 5
   tests:
@@ -13,8 +13,8 @@ jobs:
13 13
       - name: Setup - Install Yarn
14 14
         run: YARN_GPG=NO curl -o- -L https://yarnpkg.com/install.sh | bash
15 15
       - name: Setup - Install NPM Dependencies
16
-        run: yarn
16
+        run: yarn --frozen-lockfile
17 17
       - name: Setup - Install CocoaPods CLI
18
-        run: sudo gem install cocoapods
18
+        run: sudo gem install cocoapods -v 1.8.4
19 19
       - name: Run tests
20 20
         run: yarn ci

+ 108
- 0
.github/workflows/scripts/install-vs-features.ps1 查看文件

@@ -0,0 +1,108 @@
1
+param (
2
+	[Parameter(Mandatory=$true)]
3
+	[string[]] $Components,
4
+
5
+	[uri] $InstallerUri = "https://download.visualstudio.microsoft.com/download/pr/c4fef23e-cc45-4836-9544-70e213134bc8/1ee5717e9a1e05015756dff77eb27d554a79a6db91f2716d836df368381af9a1/vs_Enterprise.exe",
6
+
7
+	[string] $VsInstaller = "${env:System_DefaultWorkingDirectory}\vs_Enterprise.exe",
8
+
9
+	[string] $VsInstallOutputDir = "${env:System_DefaultWorkingDirectory}\vs",
10
+
11
+	[System.IO.FileInfo] $VsInstallPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Enterprise",
12
+
13
+	[System.IO.FileInfo] $VsInstallerPath = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer",
14
+
15
+	[switch] $Collect = $false,
16
+
17
+	[switch] $Cleanup = $false,
18
+
19
+	[switch] $UseWebInstaller = $false
20
+)
21
+
22
+$Components | ForEach-Object {
23
+	$componentList += '--add', $_
24
+}
25
+
26
+$LocalVsInstaller = "$VsInstallerPath\vs_installershell.exe"
27
+
28
+$UseWebInstaller = $UseWebInstaller -or -not (Test-Path -Path "$LocalVsInstaller")
29
+
30
+if ($UseWebInstaller) {
31
+	Write-Host "Downloading web installer..."
32
+
33
+	Invoke-WebRequest -Method Get `
34
+		-Uri $InstallerUri `
35
+		-OutFile $VsInstaller
36
+
37
+	New-Item -ItemType directory -Path $VsInstallOutputDir
38
+
39
+	Write-Host "Running web installer to download requested components..."
40
+
41
+	Start-Process `
42
+		-FilePath "$VsInstaller" `
43
+		-ArgumentList ( `
44
+			'--layout', "$VsInstallOutputDir",
45
+			'--wait',
46
+			'--norestart',
47
+			'--quiet' + `
48
+			$componentList
49
+		) `
50
+		-Wait `
51
+		-PassThru
52
+
53
+	Write-Host "Running downloaded VS installer to add requested components..."
54
+
55
+	Start-Process `
56
+		-FilePath "$VsInstallOutputDir\vs_Enterprise.exe" `
57
+		-ArgumentList (
58
+			'modify',
59
+			'--installPath', "`"$VsInstallPath`"" ,
60
+			'--wait',
61
+			'--norestart',
62
+			'--quiet' + `
63
+			$componentList
64
+		) `
65
+		-Wait `
66
+		-PassThru `
67
+		-OutVariable returnCode
68
+
69
+	if ($Cleanup) {
70
+		Write-Host "Cleaning up..."
71
+
72
+		Remove-Item -Path $VsInstaller
73
+		Remove-Item -Path $VsInstallOutputDir -Recurse
74
+	}
75
+	
76
+} else {
77
+	Write-Host "Running local installer to add requested components..."
78
+
79
+	Start-Process `
80
+		-FilePath "$LocalVsInstaller" `
81
+		-ArgumentList (
82
+			'modify',
83
+			'--installPath', "`"$VsInstallPath`"" ,
84
+			'--norestart',
85
+			'--quiet' + `
86
+			$componentList
87
+		) `
88
+		-Wait `
89
+		-OutVariable returnCode
90
+}
91
+
92
+if ($Collect) {
93
+	Invoke-WebRequest -Method Get `
94
+		-Uri 'https://download.microsoft.com/download/8/3/4/834E83F6-C377-4DCE-A757-69A418B6C6DF/Collect.exe' `
95
+		-OutFile ${env:System_DefaultWorkingDirectory}\Collect.exe
96
+
97
+	# Should generate ${env:Temp}\vslogs.zip
98
+	Start-Process `
99
+		-FilePath "${env:System_DefaultWorkingDirectory}\Collect.exe" `
100
+		-Wait `
101
+		-PassThru
102
+
103
+	New-Item -ItemType Directory -Force ${env:System_DefaultWorkingDirectory}\vslogs
104
+	Expand-Archive -Path ${env:TEMP}\vslogs.zip -DestinationPath ${env:System_DefaultWorkingDirectory}\vslogs\
105
+
106
+	Write-Host "VC versions after installation:"
107
+	Get-ChildItem -Name "$VsInstallPath\VC\Tools\MSVC\"
108
+}

+ 67
- 0
.github/workflows/windows-ci.yml 查看文件

@@ -0,0 +1,67 @@
1
+name: Windows CI
2
+on: [pull_request]
3
+
4
+jobs:
5
+  run-windows-tests:
6
+    name: Build & run tests
7
+    runs-on: windows-2019
8
+
9
+    steps:
10
+    - uses: actions/checkout@v2
11
+      name: Checkout Code
12
+     
13
+    - name: Setup Node.js
14
+      uses: actions/setup-node@v1
15
+      with:
16
+        node-version: '12.9.1'
17
+
18
+    - name: Install Visual Studio components
19
+      shell: powershell
20
+      run: .\.github\workflows\scripts\install-vs-features.ps1 Microsoft.VisualStudio.Component.VC.v141.x86.x64,Microsoft.VisualStudio.ComponentGroup.UWP.VC.v141
21
+
22
+    - name: Setup MSBuild
23
+      uses: microsoft/setup-msbuild@v1.0.0
24
+      with:
25
+        vs-version: 16.5
26
+       
27
+    - name: Setup NuGet
28
+      uses: NuGet/setup-nuget@v1.0.2
29
+
30
+    - name: Check node modules cache
31
+      uses: actions/cache@v1
32
+      id: yarn-cache
33
+      with:
34
+        path: ./node_modules
35
+        key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
36
+        restore-keys: |
37
+          ${{ runner.os }}-yarn-
38
+
39
+    - name: Install node modules
40
+      if: steps.yarn-cache.outputs.cache-hit != 'true'
41
+      run: yarn --pure-lockfile
42
+    
43
+    - name: yarn build
44
+      if: steps.yarn-cache.outputs.cache-hit == 'true'
45
+      run: |
46
+        yarn build
47
+        yarn tsc
48
+ 
49
+    - name: NuGet restore
50
+      run: nuget restore example\windows\WebViewWindows.sln
51
+
52
+    - name: Build x64 release
53
+      run: msbuild example\windows\WebViewWindows.sln /p:Configuration=Release /p:Platform=x64 -m
54
+
55
+    - name: Deploy
56
+      shell: powershell
57
+      run: |
58
+        cd example
59
+        Copy-Item -Path windows\x64\Release -Recurse -Destination windows\
60
+        npx react-native run-windows --arch x64 --release --no-build --no-packager
61
+
62
+    - name: Start Appium server
63
+      shell: powershell
64
+      run: Start-Process PowerShell -ArgumentList "yarn appium"
65
+      
66
+    - name: Run tests
67
+      run: yarn test:windows

+ 6
- 1
.gitignore 查看文件

@@ -53,4 +53,9 @@ android/gradle
53 53
 android/gradlew
54 54
 android/gradlew.bat
55 55
 
56
-lib/
56
+lib/
57
+.classpath
58
+.project
59
+.settings/
60
+msbuild.binlog
61
+example/msbuild.binlog

+ 15
- 4
README.md 查看文件

@@ -4,7 +4,8 @@
4 4
 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
5 5
 [![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors)
6 6
 [![Known Vulnerabilities](https://snyk.io/test/github/react-native-community/react-native-webview/badge.svg?style=flat-square)](https://snyk.io/test/github/react-native-community/react-native-webview)
7
-<a href="https://www.npmjs.com/package/react-native-webview"><img src="https://img.shields.io/npm/v/react-native-webview.svg"></a>
7
+[![NPM Version](https://img.shields.io/npm/v/react-native-webview.svg?style=flat-square)](https://www.npmjs.com/package/react-native-webview)
8
+[![Lean Core Extracted](https://img.shields.io/badge/Lean%20Core-Extracted-brightgreen.svg?style=flat-square)][lean-core-issue]
8 9
 
9 10
 **React Native WebView** is a modern, well-supported, and cross-platform WebView for React Native. It is intended to be a replacement for the built-in WebView (which will be [removed from core](https://github.com/react-native-community/discussions-and-proposals/pull/3)).
10 11
 
@@ -19,6 +20,8 @@ _This project is maintained for free by these people using both their free time
19 20
 
20 21
 - [x] iOS
21 22
 - [x] Android
23
+- [x] macOS
24
+- [x] Windows
22 25
 
23 26
 _Note: Expo support for React Native WebView started with [Expo SDK v33.0.0](https://blog.expo.io/expo-sdk-v33-0-0-is-now-available-52d1c99dfe4c)._
24 27
 
@@ -34,6 +37,8 @@ This project follows [semantic versioning](https://semver.org/). We do not hesit
34 37
 
35 38
 Current Version: ![version](https://img.shields.io/npm/v/react-native-webview.svg)
36 39
 
40
+- [8.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v8.0.0) - onNavigationStateChange now triggers with hash url changes
41
+
37 42
 - [7.0.1](https://github.com/react-native-community/react-native-webview/releases/tag/v7.0.1) - Removed UIWebView
38 43
 
39 44
 - [6.0.**2**](https://github.com/react-native-community/react-native-webview/releases/tag/v6.0.2) - Update to AndroidX. Make sure to enable it in your project's `android/gradle.properties`. See [Getting Started Guide](docs/Getting-Started.md).
@@ -61,9 +66,7 @@ import { WebView } from 'react-native-webview';
61 66
 // ...
62 67
 class MyWebComponent extends Component {
63 68
   render() {
64
-    return (
65
-      <WebView source={{ uri: 'https://facebook.github.io/react-native/' }} />
66
-    );
69
+    return <WebView source={{ uri: 'https://reactnative.dev/' }} />;
67 70
   }
68 71
 }
69 72
 ```
@@ -93,3 +96,11 @@ This project follows the [all-contributors](https://github.com/all-contributors/
93 96
 ## License
94 97
 
95 98
 MIT
99
+
100
+## Translations
101
+
102
+This readme is available in:
103
+
104
+- [Brazilian portuguese](docs/README.portuguese.md)
105
+
106
+[lean-core-issue]: https://github.com/facebook/react-native/issues/23313

+ 31
- 0
__tests__/Alert.test.js 查看文件

@@ -0,0 +1,31 @@
1
+/**
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+import { driver, By2 } from 'selenium-appium'
7
+import { until } from 'selenium-webdriver';
8
+
9
+const setup = require('../jest-setups/jest.setup');
10
+jest.setTimeout(50000);
11
+
12
+beforeAll(() => {
13
+  return driver.startWithCapabilities(setup.capabilites);
14
+});
15
+
16
+afterAll(() => {
17
+  return driver.quit();
18
+});
19
+
20
+describe('Alert Tests', () => {
21
+  
22
+  test('Show Alert', async () => {
23
+    const showAlertButton = await driver.wait(until.elementLocated(By2.nativeName('Show alert')));
24
+    await showAlertButton.click();
25
+    await driver.wait(until.elementLocated(By2.nativeName('Hello! I am an alert box!')));
26
+    await By2.nativeName('OK').click();
27
+    const dismissMessage = await driver.wait(until.elementLocated(By2.nativeName('Alert dismissed!')));
28
+    expect(dismissMessage).not.toBeNull();
29
+  });
30
+
31
+});

+ 24
- 15
android/build.gradle 查看文件

@@ -1,21 +1,30 @@
1 1
 buildscript {
2
-  //Buildscript is evaluated before everything else so we can't use getExtOrDefault
3
-  def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : project.properties['ReactNativeWebView_kotlinVersion']
4
-
5
-  repositories {
6
-    google()
7
-    jcenter()
2
+  ext.getExtOrDefault = {name ->
3
+    return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeWebView_' + name]
8 4
   }
9 5
 
10
-  dependencies {
11
-    classpath 'com.android.tools.build:gradle:3.2.1'
12
-    //noinspection DifferentKotlinGradleVersion
13
-    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
-  }
15
-}
6
+  // The Android Gradle plugin is only required when opening the android folder stand-alone.
7
+  // This avoids unnecessary downloads and potential conflicts when the library is included as a
8
+  // module dependency in an application project.
9
+  if (project == rootProject) {
10
+    repositories {
11
+      google()
12
+      jcenter()
13
+    }
14
+
15
+    dependencies {
16
+      classpath("com.android.tools.build:gradle:3.6.0")
17
+      classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}")
18
+    }
19
+  } else {
20
+    repositories {
21
+      jcenter()
22
+    }
16 23
 
17
-def getExtOrDefault(name) {
18
-  return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['ReactNativeWebView_' + name]
24
+    dependencies {
25
+      classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getExtOrDefault('kotlinVersion')}")
26
+    }
27
+  }
19 28
 }
20 29
 
21 30
 def getExtOrIntegerDefault(name) {
@@ -123,6 +132,6 @@ def kotlin_version = getExtOrDefault('kotlinVersion')
123 132
 
124 133
 dependencies {
125 134
   //noinspection GradleDynamicVersion
126
-  api 'com.facebook.react:react-native:+'
135
+  implementation 'com.facebook.react:react-native:+'
127 136
   implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
128 137
 }

+ 3
- 3
android/gradle.properties 查看文件

@@ -1,4 +1,4 @@
1
-ReactNativeWebView_kotlinVersion=1.3.11
2
-ReactNativeWebView_compileSdkVersion=28
3
-ReactNativeWebView_buildToolsVersion=28.0.3
1
+ReactNativeWebView_kotlinVersion=1.3.50
2
+ReactNativeWebView_compileSdkVersion=29
3
+ReactNativeWebView_buildToolsVersion=29.0.3
4 4
 ReactNativeWebView_targetSdkVersion=28

+ 201
- 32
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java 查看文件

@@ -41,12 +41,15 @@ import com.facebook.react.views.scroll.ScrollEvent;
41 41
 import com.facebook.react.views.scroll.ScrollEventType;
42 42
 import com.facebook.react.views.scroll.OnScrollDispatchHelper;
43 43
 import com.facebook.react.bridge.Arguments;
44
+import com.facebook.react.bridge.CatalystInstance;
44 45
 import com.facebook.react.bridge.LifecycleEventListener;
45 46
 import com.facebook.react.bridge.ReactContext;
46 47
 import com.facebook.react.bridge.ReadableArray;
47 48
 import com.facebook.react.bridge.ReadableMap;
48 49
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
49 50
 import com.facebook.react.bridge.WritableMap;
51
+import com.facebook.react.bridge.WritableNativeArray;
52
+import com.facebook.react.bridge.WritableNativeMap;
50 53
 import com.facebook.react.common.MapBuilder;
51 54
 import com.facebook.react.common.build.ReactBuildConfig;
52 55
 import com.facebook.react.module.annotations.ReactModule;
@@ -106,7 +109,6 @@ import javax.annotation.Nullable;
106 109
 @ReactModule(name = RNCWebViewManager.REACT_CLASS)
107 110
 public class RNCWebViewManager extends SimpleViewManager<WebView> {
108 111
 
109
-  public static String activeUrl = null;
110 112
   public static final int COMMAND_GO_BACK = 1;
111 113
   public static final int COMMAND_GO_FORWARD = 2;
112 114
   public static final int COMMAND_RELOAD = 3;
@@ -115,6 +117,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
115 117
   public static final int COMMAND_INJECT_JAVASCRIPT = 6;
116 118
   public static final int COMMAND_LOAD_URL = 7;
117 119
   public static final int COMMAND_FOCUS = 8;
120
+
121
+  // android commands
122
+  public static final int COMMAND_CLEAR_FORM_DATA = 1000;
123
+  public static final int COMMAND_CLEAR_CACHE = 1001;
124
+  public static final int COMMAND_CLEAR_HISTORY = 1002;
125
+
118 126
   protected static final String REACT_CLASS = "RNCWebView";
119 127
   protected static final String HTML_ENCODING = "UTF-8";
120 128
   protected static final String HTML_MIME_TYPE = "text/html";
@@ -188,6 +196,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
188 196
 
189 197
     webView.setDownloadListener(new DownloadListener() {
190 198
       public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
199
+        webView.setIgnoreErrFailedForThisURL(url);
200
+
191 201
         RNCWebViewModule module = getModule(reactContext);
192 202
 
193 203
         DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
@@ -202,7 +212,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
202 212
           String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
203 213
           String cookie = CookieManager.getInstance().getCookie(baseUrl);
204 214
           request.addRequestHeader("Cookie", cookie);
205
-          System.out.println("Got cookie for DownloadManager: " + cookie);
206 215
         } catch (MalformedURLException e) {
207 216
           System.out.println("Error getting cookie for DownloadManager: " + e.toString());
208 217
           e.printStackTrace();
@@ -257,15 +266,31 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
257 266
     }
258 267
   }
259 268
 
269
+  @ReactProp(name = "cacheMode")
270
+  public void setCacheMode(WebView view, String cacheModeString) {
271
+    Integer cacheMode;
272
+    switch (cacheModeString) {
273
+      case "LOAD_CACHE_ONLY":
274
+        cacheMode = WebSettings.LOAD_CACHE_ONLY;
275
+        break;
276
+      case "LOAD_CACHE_ELSE_NETWORK":
277
+        cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK;
278
+        break;
279
+      case "LOAD_NO_CACHE":
280
+        cacheMode = WebSettings.LOAD_NO_CACHE;
281
+        break;
282
+      case "LOAD_DEFAULT":
283
+      default:
284
+        cacheMode = WebSettings.LOAD_DEFAULT;
285
+        break;
286
+    }
287
+    view.getSettings().setCacheMode(cacheMode);
288
+  }
289
+
260 290
   @ReactProp(name = "androidHardwareAccelerationDisabled")
261 291
   public void setHardwareAccelerationDisabled(WebView view, boolean disabled) {
262
-    ReactContext reactContext = (ReactContext) view.getContext();
263
-    final boolean isHardwareAccelerated = (reactContext.getCurrentActivity().getWindow()
264
-        .getAttributes().flags & WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED) != 0;
265
-    if (disabled || !isHardwareAccelerated) {
292
+    if (disabled) {
266 293
       view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
267
-    } else {
268
-      view.setLayerType(View.LAYER_TYPE_HARDWARE, null);
269 294
     }
270 295
   }
271 296
 
@@ -350,6 +375,16 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
350 375
     view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
351 376
   }
352 377
 
378
+  @ReactProp(name = "javaScriptCanOpenWindowsAutomatically")
379
+  public void setJavaScriptCanOpenWindowsAutomatically(WebView view, boolean enabled) {
380
+    view.getSettings().setJavaScriptCanOpenWindowsAutomatically(enabled);
381
+  }
382
+
383
+  @ReactProp(name = "allowFileAccessFromFileURLs")
384
+  public void setAllowFileAccessFromFileURLs(WebView view, boolean allow) {
385
+    view.getSettings().setAllowFileAccessFromFileURLs(allow);
386
+  }
387
+
353 388
   @ReactProp(name = "allowUniversalAccessFromFileURLs")
354 389
   public void setAllowUniversalAccessFromFileURLs(WebView view, boolean allow) {
355 390
     view.getSettings().setAllowUniversalAccessFromFileURLs(allow);
@@ -369,7 +404,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
369 404
   public void setMessagingEnabled(WebView view, boolean enabled) {
370 405
     ((RNCWebView) view).setMessagingEnabled(enabled);
371 406
   }
372
-   
407
+
408
+  @ReactProp(name = "messagingModuleName")
409
+  public void setMessagingModuleName(WebView view, String moduleName) {
410
+    ((RNCWebView) view).setMessagingModuleName(moduleName);
411
+  }
412
+
373 413
   @ReactProp(name = "incognito")
374 414
   public void setIncognito(WebView view, boolean enabled) {
375 415
     // Remove all previous cookies
@@ -524,17 +564,19 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
524 564
   @Override
525 565
   public @Nullable
526 566
   Map<String, Integer> getCommandsMap() {
527
-    Map map = MapBuilder.of(
528
-      "goBack", COMMAND_GO_BACK,
529
-      "goForward", COMMAND_GO_FORWARD,
530
-      "reload", COMMAND_RELOAD,
531
-      "stopLoading", COMMAND_STOP_LOADING,
532
-      "postMessage", COMMAND_POST_MESSAGE,
533
-      "injectJavaScript", COMMAND_INJECT_JAVASCRIPT,
534
-      "loadUrl", COMMAND_LOAD_URL
535
-    );
536
-    map.put("requestFocus", COMMAND_FOCUS);
537
-    return map;
567
+    return MapBuilder.<String, Integer>builder()
568
+      .put("goBack", COMMAND_GO_BACK)
569
+      .put("goForward", COMMAND_GO_FORWARD)
570
+      .put("reload", COMMAND_RELOAD)
571
+      .put("stopLoading", COMMAND_STOP_LOADING)
572
+      .put("postMessage", COMMAND_POST_MESSAGE)
573
+      .put("injectJavaScript", COMMAND_INJECT_JAVASCRIPT)
574
+      .put("loadUrl", COMMAND_LOAD_URL)
575
+      .put("requestFocus", COMMAND_FOCUS)
576
+      .put("clearFormData", COMMAND_CLEAR_FORM_DATA)
577
+      .put("clearCache", COMMAND_CLEAR_CACHE)
578
+      .put("clearHistory", COMMAND_CLEAR_HISTORY)
579
+      .build();
538 580
   }
539 581
 
540 582
   @Override
@@ -580,11 +622,22 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
580 622
         if (args == null) {
581 623
           throw new RuntimeException("Arguments for loading an url are null!");
582 624
         }
625
+        ((RNCWebView) root).progressChangedFilter.setWaitingForCommandLoadUrl(false);
583 626
         root.loadUrl(args.getString(0));
584 627
         break;
585 628
       case COMMAND_FOCUS:
586 629
         root.requestFocus();
587 630
         break;
631
+      case COMMAND_CLEAR_FORM_DATA:
632
+        root.clearFormData();
633
+        break;
634
+      case COMMAND_CLEAR_CACHE:
635
+        boolean includeDiskFiles = args != null && args.getBoolean(0);
636
+        root.clearCache(includeDiskFiles);
637
+        break;
638
+      case COMMAND_CLEAR_HISTORY:
639
+        root.clearHistory();
640
+        break;
588 641
     }
589 642
   }
590 643
 
@@ -603,6 +656,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
603 656
     if (mAllowsFullscreenVideo) {
604 657
       int initialRequestedOrientation = reactContext.getCurrentActivity().getRequestedOrientation();
605 658
       mWebChromeClient = new RNCWebChromeClient(reactContext, webView) {
659
+        @Override
660
+        public Bitmap getDefaultVideoPoster() {
661
+          return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
662
+        }
663
+
606 664
         @Override
607 665
         public void onShowCustomView(View view, CustomViewCallback callback) {
608 666
           if (mVideoView != null) {
@@ -655,7 +713,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
655 713
       if (mWebChromeClient != null) {
656 714
         mWebChromeClient.onHideCustomView();
657 715
       }
658
-      mWebChromeClient = new RNCWebChromeClient(reactContext, webView);
716
+      mWebChromeClient = new RNCWebChromeClient(reactContext, webView) {
717
+        @Override
718
+        public Bitmap getDefaultVideoPoster() {
719
+          return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
720
+        }
721
+      };
659 722
       webView.setWebChromeClient(mWebChromeClient);
660 723
     }
661 724
   }
@@ -665,6 +728,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
665 728
     protected boolean mLastLoadFailed = false;
666 729
     protected @Nullable
667 730
     ReadableArray mUrlPrefixesForDefaultIntent;
731
+    protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
732
+    protected @Nullable String ignoreErrFailedForThisURL = null;
733
+
734
+    public void setIgnoreErrFailedForThisURL(@Nullable String url) {
735
+      ignoreErrFailedForThisURL = url;
736
+    }
668 737
 
669 738
     @Override
670 739
     public void onPageFinished(WebView webView, String url) {
@@ -693,7 +762,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
693 762
 
694 763
     @Override
695 764
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
696
-      activeUrl = url;
765
+      progressChangedFilter.setWaitingForCommandLoadUrl(true);
697 766
       dispatchEvent(
698 767
         view,
699 768
         new TopShouldStartLoadWithRequestEvent(
@@ -716,6 +785,21 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
716 785
       int errorCode,
717 786
       String description,
718 787
       String failingUrl) {
788
+
789
+      if (ignoreErrFailedForThisURL != null
790
+          && failingUrl.equals(ignoreErrFailedForThisURL)
791
+          && errorCode == -1
792
+          && description.equals("net::ERR_FAILED")) {
793
+
794
+        // This is a workaround for a bug in the WebView.
795
+        // See these chromium issues for more context:
796
+        // https://bugs.chromium.org/p/chromium/issues/detail?id=1023678
797
+        // https://bugs.chromium.org/p/chromium/issues/detail?id=1050635
798
+        // This entire commit should be reverted once this bug is resolved in chromium.
799
+        setIgnoreErrFailedForThisURL(null);
800
+        return;
801
+      }
802
+
719 803
       super.onReceivedError(webView, errorCode, description, failingUrl);
720 804
       mLastLoadFailed = true;
721 805
 
@@ -775,6 +859,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
775 859
     public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
776 860
       mUrlPrefixesForDefaultIntent = specialUrls;
777 861
     }
862
+
863
+    public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
864
+      progressChangedFilter = filter;
865
+    }
778 866
   }
779 867
 
780 868
   protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
@@ -796,6 +884,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
796 884
     protected View mVideoView;
797 885
     protected WebChromeClient.CustomViewCallback mCustomViewCallback;
798 886
 
887
+    protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
888
+
799 889
     public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
800 890
       this.mReactContext = reactContext;
801 891
       this.mWebView = webView;
@@ -811,6 +901,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
811 901
     }
812 902
 
813 903
     // Fix WebRTC permission request error.
904
+    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
814 905
     @Override
815 906
     public void onPermissionRequest(final PermissionRequest request) {
816 907
       String[] requestedResources = request.getResources();
@@ -849,11 +940,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
849 940
     public void onProgressChanged(WebView webView, int newProgress) {
850 941
       super.onProgressChanged(webView, newProgress);
851 942
       final String url = webView.getUrl();
852
-      if (
853
-        url != null
854
-        && activeUrl != null
855
-        && !url.equals(activeUrl)
856
-      ) {
943
+      if (progressChangedFilter.isWaitingForCommandLoadUrl()) {
857 944
         return;
858 945
       }
859 946
       WritableMap event = Arguments.createMap();
@@ -892,8 +979,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
892 979
     public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
893 980
       String[] acceptTypes = fileChooserParams.getAcceptTypes();
894 981
       boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
895
-      Intent intent = fileChooserParams.createIntent();
896
-      return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
982
+      return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptTypes, allowMultiple);
897 983
     }
898 984
 
899 985
     @Override
@@ -912,6 +998,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
912 998
     protected ViewGroup getRootView() {
913 999
       return (ViewGroup) mReactContext.getCurrentActivity().findViewById(android.R.id.content);
914 1000
     }
1001
+
1002
+    public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
1003
+      progressChangedFilter = filter;
1004
+    }
915 1005
   }
916 1006
 
917 1007
   /**
@@ -923,10 +1013,15 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
923 1013
     String injectedJS;
924 1014
     protected boolean messagingEnabled = false;
925 1015
     protected @Nullable
1016
+    String messagingModuleName;
1017
+    protected @Nullable
926 1018
     RNCWebViewClient mRNCWebViewClient;
1019
+    protected @Nullable
1020
+    CatalystInstance mCatalystInstance;
927 1021
     protected boolean sendContentSizeChangeEvents = false;
928 1022
     private OnScrollDispatchHelper mOnScrollDispatchHelper;
929 1023
     protected boolean hasScrollEvent = false;
1024
+    protected ProgressChangedFilter progressChangedFilter;
930 1025
 
931 1026
     /**
932 1027
      * WebView must be created with an context of the current activity
@@ -936,6 +1031,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
936 1031
      */
937 1032
     public RNCWebView(ThemedReactContext reactContext) {
938 1033
       super(reactContext);
1034
+      progressChangedFilter = new ProgressChangedFilter();
1035
+    }
1036
+
1037
+    public void setIgnoreErrFailedForThisURL(String url) {
1038
+      mRNCWebViewClient.setIgnoreErrFailedForThisURL(url);
939 1039
     }
940 1040
 
941 1041
     public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
@@ -980,7 +1080,20 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
980 1080
     @Override
981 1081
     public void setWebViewClient(WebViewClient client) {
982 1082
       super.setWebViewClient(client);
983
-      mRNCWebViewClient = (RNCWebViewClient) client;
1083
+      if (client instanceof RNCWebViewClient) {
1084
+        mRNCWebViewClient = (RNCWebViewClient) client;
1085
+        mRNCWebViewClient.setProgressChangedFilter(progressChangedFilter);
1086
+      }
1087
+    }
1088
+
1089
+    WebChromeClient mWebChromeClient;
1090
+    @Override
1091
+    public void setWebChromeClient(WebChromeClient client) {
1092
+      this.mWebChromeClient = client;
1093
+      super.setWebChromeClient(client);
1094
+      if (client instanceof RNCWebChromeClient) {
1095
+        ((RNCWebChromeClient) client).setProgressChangedFilter(progressChangedFilter);
1096
+      }
984 1097
     }
985 1098
 
986 1099
     public @Nullable
@@ -996,6 +1109,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
996 1109
       return new RNCWebViewBridge(webView);
997 1110
     }
998 1111
 
1112
+    protected void createCatalystInstance() {
1113
+      ReactContext reactContext = (ReactContext) this.getContext();
1114
+
1115
+      if (reactContext != null) {
1116
+        mCatalystInstance = reactContext.getCatalystInstance();
1117
+      }
1118
+    }
1119
+
999 1120
     @SuppressLint("AddJavascriptInterface")
1000 1121
     public void setMessagingEnabled(boolean enabled) {
1001 1122
       if (messagingEnabled == enabled) {
@@ -1006,11 +1127,16 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1006 1127
 
1007 1128
       if (enabled) {
1008 1129
         addJavascriptInterface(createRNCWebViewBridge(this), JAVASCRIPT_INTERFACE);
1130
+        this.createCatalystInstance();
1009 1131
       } else {
1010 1132
         removeJavascriptInterface(JAVASCRIPT_INTERFACE);
1011 1133
       }
1012 1134
     }
1013 1135
 
1136
+    public void setMessagingModuleName(String moduleName) {
1137
+      messagingModuleName = moduleName;
1138
+    }
1139
+
1014 1140
     protected void evaluateJavascriptWithFallback(String script) {
1015 1141
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
1016 1142
         evaluateJavascript(script, null);
@@ -1034,6 +1160,9 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1034 1160
     }
1035 1161
 
1036 1162
     public void onMessage(String message) {
1163
+      ReactContext reactContext = (ReactContext) this.getContext();
1164
+      RNCWebView mContext = this;
1165
+
1037 1166
       if (mRNCWebViewClient != null) {
1038 1167
         WebView webView = this;
1039 1168
         webView.post(new Runnable() {
@@ -1044,16 +1173,36 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1044 1173
             }
1045 1174
             WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
1046 1175
             data.putString("data", message);
1047
-            dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
1176
+
1177
+            if (mCatalystInstance != null) {
1178
+              mContext.sendDirectMessage(data);
1179
+            } else {
1180
+              dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
1181
+            }
1048 1182
           }
1049 1183
         });
1050 1184
       } else {
1051 1185
         WritableMap eventData = Arguments.createMap();
1052 1186
         eventData.putString("data", message);
1053
-        dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
1187
+
1188
+        if (mCatalystInstance != null) {
1189
+          this.sendDirectMessage(eventData);
1190
+        } else {
1191
+          dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
1192
+        }
1054 1193
       }
1055 1194
     }
1056 1195
 
1196
+    protected void sendDirectMessage(WritableMap data) {
1197
+      WritableNativeMap event = new WritableNativeMap();
1198
+      event.putMap("nativeEvent", data);
1199
+
1200
+      WritableNativeArray params = new WritableNativeArray();
1201
+      params.pushMap(event);
1202
+
1203
+      mCatalystInstance.callFunction(messagingModuleName, "onMessage", params);
1204
+    }
1205
+
1057 1206
     protected void onScrollChanged(int x, int y, int oldX, int oldY) {
1058 1207
       super.onScrollChanged(x, y, oldX, oldY);
1059 1208
 
@@ -1087,6 +1236,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1087 1236
       destroy();
1088 1237
     }
1089 1238
 
1239
+    @Override
1240
+    public void destroy() {
1241
+      if (mWebChromeClient != null) {
1242
+        mWebChromeClient.onHideCustomView();
1243
+      }
1244
+      super.destroy();
1245
+    }
1246
+
1090 1247
     protected class RNCWebViewBridge {
1091 1248
       RNCWebView mContext;
1092 1249
 
@@ -1103,5 +1260,17 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1103 1260
         mContext.onMessage(message);
1104 1261
       }
1105 1262
     }
1263
+
1264
+    protected static class ProgressChangedFilter {
1265
+      private boolean waitingForCommandLoadUrl = false;
1266
+
1267
+      public void setWaitingForCommandLoadUrl(boolean isWaiting) {
1268
+        waitingForCommandLoadUrl = isWaiting;
1269
+      }
1270
+
1271
+      public boolean isWaitingForCommandLoadUrl() {
1272
+        return waitingForCommandLoadUrl;
1273
+      }
1274
+    }
1106 1275
   }
1107 1276
 }

+ 174
- 75
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewModule.java 查看文件

@@ -11,9 +11,11 @@ import android.os.Build;
11 11
 import android.os.Environment;
12 12
 import android.os.Parcelable;
13 13
 import android.provider.MediaStore;
14
+
14 15
 import androidx.annotation.RequiresApi;
15 16
 import androidx.core.content.ContextCompat;
16 17
 import androidx.core.content.FileProvider;
18
+
17 19
 import android.util.Log;
18 20
 import android.webkit.MimeTypeMap;
19 21
 import android.webkit.ValueCallback;
@@ -32,6 +34,7 @@ import com.facebook.react.modules.core.PermissionListener;
32 34
 import java.io.File;
33 35
 import java.io.IOException;
34 36
 import java.util.ArrayList;
37
+import java.util.Arrays;
35 38
 
36 39
 import static android.app.Activity.RESULT_OK;
37 40
 
@@ -41,11 +44,24 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
41 44
   private static final int PICKER = 1;
42 45
   private static final int PICKER_LEGACY = 3;
43 46
   private static final int FILE_DOWNLOAD_PERMISSION_REQUEST = 1;
44
-  final String DEFAULT_MIME_TYPES = "*/*";
45 47
   private ValueCallback<Uri> filePathCallbackLegacy;
46 48
   private ValueCallback<Uri[]> filePathCallback;
47
-  private Uri outputFileUri;
49
+  private File outputImage;
50
+  private File outputVideo;
48 51
   private DownloadManager.Request downloadRequest;
52
+
53
+  private enum MimeType {
54
+    DEFAULT("*/*"),
55
+    IMAGE("image"),
56
+    VIDEO("video");
57
+
58
+    private final String value;
59
+
60
+    MimeType(String value) {
61
+      this.value = value;
62
+    }
63
+  }
64
+
49 65
   private PermissionListener webviewFileDownloaderPermissionListener = new PermissionListener() {
50 66
     @Override
51 67
     public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
@@ -95,6 +111,16 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
95 111
       return;
96 112
     }
97 113
 
114
+    boolean imageTaken = false;
115
+    boolean videoTaken = false;
116
+
117
+    if (outputImage != null && outputImage.length() > 0) {
118
+      imageTaken = true;
119
+    }
120
+    if (outputVideo != null && outputVideo.length() > 0) {
121
+      videoTaken = true;
122
+    }
123
+
98 124
     // based off of which button was pressed, we get an activity result and a file
99 125
     // the camera activity doesn't properly return the filename* (I think?) so we use
100 126
     // this filename instead
@@ -105,23 +131,42 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
105 131
             filePathCallback.onReceiveValue(null);
106 132
           }
107 133
         } else {
108
-          Uri result[] = this.getSelectedFiles(data, resultCode);
109
-          if (result != null) {
110
-            filePathCallback.onReceiveValue(result);
134
+          if (imageTaken) {
135
+            filePathCallback.onReceiveValue(new Uri[]{getOutputUri(outputImage)});
136
+          } else if (videoTaken) {
137
+            filePathCallback.onReceiveValue(new Uri[]{getOutputUri(outputVideo)});
111 138
           } else {
112
-            filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
139
+            filePathCallback.onReceiveValue(this.getSelectedFiles(data, resultCode));
113 140
           }
114 141
         }
115 142
         break;
116 143
       case PICKER_LEGACY:
117
-        Uri result = resultCode != Activity.RESULT_OK ? null : data == null ? outputFileUri : data.getData();
118
-        filePathCallbackLegacy.onReceiveValue(result);
144
+        if (resultCode != RESULT_OK) {
145
+          filePathCallbackLegacy.onReceiveValue(null);
146
+        } else {
147
+          if (imageTaken) {
148
+            filePathCallbackLegacy.onReceiveValue(getOutputUri(outputImage));
149
+          } else if (videoTaken) {
150
+            filePathCallbackLegacy.onReceiveValue(getOutputUri(outputVideo));
151
+          } else {
152
+            filePathCallbackLegacy.onReceiveValue(data.getData());
153
+          }
154
+        }
119 155
         break;
120 156
 
121 157
     }
158
+
159
+    if (outputImage != null && !imageTaken) {
160
+      outputImage.delete();
161
+    }
162
+    if (outputVideo != null && !videoTaken) {
163
+      outputVideo.delete();
164
+    }
165
+
122 166
     filePathCallback = null;
123 167
     filePathCallbackLegacy = null;
124
-    outputFileUri = null;
168
+    outputImage = null;
169
+    outputVideo = null;
125 170
   }
126 171
 
127 172
   public void onNewIntent(Intent intent) {
@@ -132,15 +177,6 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
132 177
       return null;
133 178
     }
134 179
 
135
-    // we have one file selected
136
-    if (data.getData() != null) {
137
-      if (resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
138
-        return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
139
-      } else {
140
-        return null;
141
-      }
142
-    }
143
-
144 180
     // we have multiple files selected
145 181
     if (data.getClipData() != null) {
146 182
       final int numSelectedFiles = data.getClipData().getItemCount();
@@ -150,6 +186,12 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
150 186
       }
151 187
       return result;
152 188
     }
189
+
190
+    // we have one file selected
191
+    if (data.getData() != null && resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
192
+      return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
193
+    }
194
+
153 195
     return null;
154 196
   }
155 197
 
@@ -161,10 +203,16 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
161 203
 
162 204
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
163 205
     if (acceptsImages(acceptType)) {
164
-      extraIntents.add(getPhotoIntent());
206
+      Intent photoIntent = getPhotoIntent();
207
+      if (photoIntent != null) {
208
+        extraIntents.add(photoIntent);
209
+      }
165 210
     }
166 211
     if (acceptsVideo(acceptType)) {
167
-      extraIntents.add(getVideoIntent());
212
+      Intent videoIntent = getVideoIntent();
213
+      if (videoIntent != null) {
214
+        extraIntents.add(videoIntent);
215
+      }
168 216
     }
169 217
     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
170 218
 
@@ -176,15 +224,23 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
176 224
   }
177 225
 
178 226
   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
179
-  public boolean startPhotoPickerIntent(final ValueCallback<Uri[]> callback, final Intent intent, final String[] acceptTypes, final boolean allowMultiple) {
227
+  public boolean startPhotoPickerIntent(final ValueCallback<Uri[]> callback, final String[] acceptTypes, final boolean allowMultiple) {
180 228
     filePathCallback = callback;
181 229
 
182 230
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
183
-    if (acceptsImages(acceptTypes)) {
184
-      extraIntents.add(getPhotoIntent());
185
-    }
186
-    if (acceptsVideo(acceptTypes)) {
187
-      extraIntents.add(getVideoIntent());
231
+    if (!needsCameraPermission()) {
232
+      if (acceptsImages(acceptTypes)) {
233
+        Intent photoIntent = getPhotoIntent();
234
+        if (photoIntent != null) {
235
+          extraIntents.add(photoIntent);
236
+        }
237
+      }
238
+      if (acceptsVideo(acceptTypes)) {
239
+        Intent videoIntent = getVideoIntent();
240
+        if (videoIntent != null) {
241
+          extraIntents.add(videoIntent);
242
+        }
243
+      }
188 244
     }
189 245
 
190 246
     Intent fileSelectionIntent = getFileChooserIntent(acceptTypes, allowMultiple);
@@ -216,16 +272,13 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
216 272
   }
217 273
 
218 274
   public boolean grantFileDownloaderPermissions() {
219
-    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
275
+    // Permission not required for Android Q and above
276
+    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
220 277
       return true;
221 278
     }
222 279
 
223
-    boolean result = true;
224
-    if (ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
225
-      result = false;
226
-    }
227
-
228
-    if (!result) {
280
+    boolean result = ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
281
+    if (!result && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
229 282
       PermissionAwareActivity activity = getPermissionAwareActivity();
230 283
       activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, FILE_DOWNLOAD_PERMISSION_REQUEST, webviewFileDownloaderPermissionListener);
231 284
     }
@@ -233,26 +286,59 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
233 286
     return result;
234 287
   }
235 288
 
289
+  protected boolean needsCameraPermission() {
290
+    boolean needed = false;
291
+
292
+    PackageManager packageManager = getCurrentActivity().getPackageManager();
293
+    try {
294
+      String[] requestedPermissions = packageManager.getPackageInfo(getReactApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions;
295
+      if (Arrays.asList(requestedPermissions).contains(Manifest.permission.CAMERA)
296
+        && ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
297
+        needed = true;
298
+      }
299
+    } catch (PackageManager.NameNotFoundException e) {
300
+      needed = true;
301
+    }
302
+
303
+    return needed;
304
+  }
305
+
236 306
   private Intent getPhotoIntent() {
237
-    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
238
-    outputFileUri = getOutputUri(MediaStore.ACTION_IMAGE_CAPTURE);
239
-    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
307
+    Intent intent = null;
308
+
309
+    try {
310
+      outputImage = getCapturedFile(MimeType.IMAGE);
311
+      Uri outputImageUri = getOutputUri(outputImage);
312
+      intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
313
+      intent.putExtra(MediaStore.EXTRA_OUTPUT, outputImageUri);
314
+    } catch (IOException | IllegalArgumentException e) {
315
+      Log.e("CREATE FILE", "Error occurred while creating the File", e);
316
+      e.printStackTrace();
317
+    }
318
+
240 319
     return intent;
241 320
   }
242 321
 
243 322
   private Intent getVideoIntent() {
244
-    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
245
-    // @todo from experience, for Videos we get the data onActivityResult
246
-    // so there's no need to store the Uri
247
-    outputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE);
248
-    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
323
+    Intent intent = null;
324
+
325
+    try {
326
+      outputVideo = getCapturedFile(MimeType.VIDEO);
327
+      Uri outputVideoUri = getOutputUri(outputVideo);
328
+      intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
329
+      intent.putExtra(MediaStore.EXTRA_OUTPUT, outputVideoUri);
330
+    } catch (IOException | IllegalArgumentException e) {
331
+      Log.e("CREATE FILE", "Error occurred while creating the File", e);
332
+      e.printStackTrace();
333
+    }
334
+    
249 335
     return intent;
250 336
   }
251 337
 
252 338
   private Intent getFileChooserIntent(String acceptTypes) {
253 339
     String _acceptTypes = acceptTypes;
254 340
     if (acceptTypes.isEmpty()) {
255
-      _acceptTypes = DEFAULT_MIME_TYPES;
341
+      _acceptTypes = MimeType.DEFAULT.value;
256 342
     }
257 343
     if (acceptTypes.matches("\\.\\w+")) {
258 344
       _acceptTypes = getMimeTypeFromExtension(acceptTypes.replace(".", ""));
@@ -266,7 +352,7 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
266 352
   private Intent getFileChooserIntent(String[] acceptTypes, boolean allowMultiple) {
267 353
     Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
268 354
     intent.addCategory(Intent.CATEGORY_OPENABLE);
269
-    intent.setType("*/*");
355
+    intent.setType(MimeType.DEFAULT.value);
270 356
     intent.putExtra(Intent.EXTRA_MIME_TYPES, getAcceptedMimeType(acceptTypes));
271 357
     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
272 358
     return intent;
@@ -277,25 +363,33 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
277 363
     if (types.matches("\\.\\w+")) {
278 364
       mimeType = getMimeTypeFromExtension(types.replace(".", ""));
279 365
     }
280
-    return mimeType.isEmpty() || mimeType.toLowerCase().contains("image");
366
+    return mimeType.isEmpty() || mimeType.toLowerCase().contains(MimeType.IMAGE.value);
281 367
   }
282 368
 
283 369
   private Boolean acceptsImages(String[] types) {
284 370
     String[] mimeTypes = getAcceptedMimeType(types);
285
-    return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "image");
371
+    return arrayContainsString(mimeTypes, MimeType.DEFAULT.value) || arrayContainsString(mimeTypes, MimeType.IMAGE.value);
286 372
   }
287 373
 
288 374
   private Boolean acceptsVideo(String types) {
375
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
376
+      return false;
377
+    }
378
+
289 379
     String mimeType = types;
290 380
     if (types.matches("\\.\\w+")) {
291 381
       mimeType = getMimeTypeFromExtension(types.replace(".", ""));
292 382
     }
293
-    return mimeType.isEmpty() || mimeType.toLowerCase().contains("video");
383
+    return mimeType.isEmpty() || mimeType.toLowerCase().contains(MimeType.VIDEO.value);
294 384
   }
295 385
 
296 386
   private Boolean acceptsVideo(String[] types) {
387
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
388
+      return false;
389
+    }
390
+
297 391
     String[] mimeTypes = getAcceptedMimeType(types);
298
-    return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "video");
392
+    return arrayContainsString(mimeTypes, MimeType.DEFAULT.value) || arrayContainsString(mimeTypes, MimeType.VIDEO.value);
299 393
   }
300 394
 
301 395
   private Boolean arrayContainsString(String[] array, String pattern) {
@@ -308,8 +402,8 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
308 402
   }
309 403
 
310 404
   private String[] getAcceptedMimeType(String[] types) {
311
-    if (isArrayEmpty(types)) {
312
-      return new String[]{DEFAULT_MIME_TYPES};
405
+    if (noAcceptTypesSet(types)) {
406
+      return new String[]{MimeType.DEFAULT.value};
313 407
     }
314 408
     String[] mimeTypes = new String[types.length];
315 409
     for (int i = 0; i < types.length; i++) {
@@ -317,7 +411,11 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
317 411
       // convert file extensions to mime types
318 412
       if (t.matches("\\.\\w+")) {
319 413
         String mimeType = getMimeTypeFromExtension(t.replace(".", ""));
320
-        mimeTypes[i] = mimeType;
414
+        if(mimeType != null) {
415
+          mimeTypes[i] = mimeType;
416
+        } else {
417
+          mimeTypes[i] = t;
418
+        }
321 419
       } else {
322 420
         mimeTypes[i] = t;
323 421
       }
@@ -333,15 +431,7 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
333 431
     return type;
334 432
   }
335 433
 
336
-  private Uri getOutputUri(String intentType) {
337
-    File capturedFile = null;
338
-    try {
339
-      capturedFile = getCapturedFile(intentType);
340
-    } catch (IOException e) {
341
-      Log.e("CREATE FILE", "Error occurred while creating the File", e);
342
-      e.printStackTrace();
343
-    }
344
-
434
+  private Uri getOutputUri(File capturedFile) {
345 435
     // for versions below 6.0 (23) we use the old File creation & permissions model
346 436
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
347 437
       return Uri.fromFile(capturedFile);
@@ -352,41 +442,50 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
352 442
     return FileProvider.getUriForFile(getReactApplicationContext(), packageName + ".fileprovider", capturedFile);
353 443
   }
354 444
 
355
-  private File getCapturedFile(String intentType) throws IOException {
445
+  private File getCapturedFile(MimeType mimeType) throws IOException {
356 446
     String prefix = "";
357 447
     String suffix = "";
358 448
     String dir = "";
359
-    String filename = "";
360 449
 
361
-    if (intentType.equals(MediaStore.ACTION_IMAGE_CAPTURE)) {
362
-      prefix = "image-";
363
-      suffix = ".jpg";
364
-      dir = Environment.DIRECTORY_PICTURES;
365
-    } else if (intentType.equals(MediaStore.ACTION_VIDEO_CAPTURE)) {
366
-      prefix = "video-";
367
-      suffix = ".mp4";
368
-      dir = Environment.DIRECTORY_MOVIES;
450
+    switch (mimeType) {
451
+      case IMAGE:
452
+        prefix = "image-";
453
+        suffix = ".jpg";
454
+        dir = Environment.DIRECTORY_PICTURES;
455
+        break;
456
+      case VIDEO:
457
+        prefix = "video-";
458
+        suffix = ".mp4";
459
+        dir = Environment.DIRECTORY_MOVIES;
460
+        break;
461
+
462
+      default:
463
+        break;
369 464
     }
370 465
 
371
-    filename = prefix + String.valueOf(System.currentTimeMillis()) + suffix;
466
+    String filename = prefix + String.valueOf(System.currentTimeMillis()) + suffix;
467
+    File outputFile = null;
372 468
 
373 469
     // for versions below 6.0 (23) we use the old File creation & permissions model
374 470
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
375 471
       // only this Directory works on all tested Android versions
376 472
       // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
377 473
       File storageDir = Environment.getExternalStoragePublicDirectory(dir);
378
-      return new File(storageDir, filename);
474
+      outputFile = new File(storageDir, filename);
475
+    } else {
476
+      File storageDir = getReactApplicationContext().getExternalFilesDir(null);
477
+      outputFile = File.createTempFile(prefix, suffix, storageDir);
379 478
     }
380 479
 
381
-    File storageDir = getReactApplicationContext().getExternalFilesDir(null);
382
-    return File.createTempFile(filename, suffix, storageDir);
480
+    return outputFile;
383 481
   }
384 482
 
385
-  private Boolean isArrayEmpty(String[] arr) {
483
+  private Boolean noAcceptTypesSet(String[] types) {
386 484
     // when our array returned from getAcceptTypes() has no values set from the webview
387 485
     // i.e. <input type="file" />, without any "accept" attr
388 486
     // will be an array with one empty string element, afaik
389
-    return arr.length == 0 || (arr.length == 1 && arr[0].length() == 0);
487
+
488
+    return types.length == 0 || (types.length == 1 && types[0] != null && types[0].length() == 0);
390 489
   }
391 490
 
392 491
   private PermissionAwareActivity getPermissionAwareActivity() {

+ 0
- 27
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java 查看文件

@@ -1,27 +0,0 @@
1
-package com.reactnativecommunity.webview;
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.Collections;
10
-import java.util.List;
11
-
12
-public class RNCWebViewPackage implements ReactPackage {
13
-  @Override
14
-  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
15
-    return Collections.singletonList(new RNCWebViewModule(reactContext));
16
-  }
17
-
18
-  // Deprecated from RN 0.47
19
-  public List<Class<? extends JavaScriptModule>> createJSModules() {
20
-    return Collections.emptyList();
21
-  }
22
-
23
-  @Override
24
-  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
25
-    return Collections.singletonList(new RNCWebViewManager());
26
-  }
27
-}

+ 15
- 0
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.kt 查看文件

@@ -0,0 +1,15 @@
1
+package com.reactnativecommunity.webview
2
+
3
+import com.facebook.react.ReactPackage
4
+import com.facebook.react.bridge.ReactApplicationContext
5
+
6
+
7
+class RNCWebViewPackage: ReactPackage {
8
+  override fun createNativeModules(reactContext: ReactApplicationContext) = listOf(
9
+    RNCWebViewModule(reactContext)
10
+  )
11
+
12
+  override fun createViewManagers(reactContext: ReactApplicationContext) = listOf(
13
+    RNCWebViewManager()
14
+  )
15
+}

ios/RNCWKProcessPoolManager.h → apple/RNCWKProcessPoolManager.h 查看文件


ios/RNCWKProcessPoolManager.m → apple/RNCWKProcessPoolManager.m 查看文件


ios/RNCWebView.h → apple/RNCWebView.h 查看文件

@@ -19,12 +19,20 @@
19 19
 
20 20
 @end
21 21
 
22
+@interface RNCWeakScriptMessageDelegate : NSObject<WKScriptMessageHandler>
23
+@property (nonatomic, weak) id<WKScriptMessageHandler> scriptDelegate;
24
+- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate;
25
+@end
26
+
22 27
 @interface RNCWebView : RCTView
23 28
 
24 29
 @property (nonatomic, weak) id<RNCWebViewDelegate> _Nullable delegate;
25 30
 @property (nonatomic, copy) NSDictionary * _Nullable source;
26 31
 @property (nonatomic, assign) BOOL messagingEnabled;
27 32
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScript;
33
+@property (nonatomic, copy) NSString * _Nullable injectedJavaScriptBeforeContentLoaded;
34
+@property (nonatomic, assign) BOOL injectedJavaScriptForMainFrameOnly;
35
+@property (nonatomic, assign) BOOL injectedJavaScriptBeforeContentLoadedForMainFrameOnly;
28 36
 @property (nonatomic, assign) BOOL scrollEnabled;
29 37
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
30 38
 @property (nonatomic, assign) BOOL autoManageStatusBarEnabled;
@@ -47,10 +55,13 @@
47 55
 @property (nonatomic, copy) NSString * _Nullable applicationNameForUserAgent;
48 56
 @property (nonatomic, assign) BOOL cacheEnabled;
49 57
 @property (nonatomic, assign) BOOL javaScriptEnabled;
58
+@property (nonatomic, assign) BOOL javaScriptCanOpenWindowsAutomatically;
59
+@property (nonatomic, assign) BOOL allowFileAccessFromFileURLs;
50 60
 @property (nonatomic, assign) BOOL allowsLinkPreview;
51 61
 @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;
52 62
 @property (nonatomic, assign) BOOL showsVerticalScrollIndicator;
53 63
 @property (nonatomic, assign) BOOL directionalLockEnabled;
64
+@property (nonatomic, assign) BOOL ignoreSilentHardwareSwitch;
54 65
 @property (nonatomic, copy) NSString * _Nullable allowingReadAccessToURL;
55 66
 
56 67
 + (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential;

ios/RNCWebView.m → apple/RNCWebView.m 查看文件

@@ -9,26 +9,66 @@
9 9
 #import <React/RCTConvert.h>
10 10
 #import <React/RCTAutoInsetsProtocol.h>
11 11
 #import "RNCWKProcessPoolManager.h"
12
+#if !TARGET_OS_OSX
12 13
 #import <UIKit/UIKit.h>
14
+#else
15
+#import <React/RCTUIKit.h>
16
+#endif // !TARGET_OS_OSX
13 17
 
14 18
 #import "objc/runtime.h"
15 19
 
16 20
 static NSTimer *keyboardTimer;
21
+static NSString *const HistoryShimName = @"ReactNativeHistoryShim";
17 22
 static NSString *const MessageHandlerName = @"ReactNativeWebView";
18 23
 static NSURLCredential* clientAuthenticationCredential;
19 24
 static NSDictionary* customCertificatesForHost;
20 25
 
26
+#if !TARGET_OS_OSX
21 27
 // runtime trick to remove WKWebView keyboard default toolbar
22 28
 // see: http://stackoverflow.com/questions/19033292/ios-7-uiwebview-keyboard-issue/19042279#19042279
23
-@interface _SwizzleHelperWK : NSObject @end
29
+@interface _SwizzleHelperWK : UIView
30
+@property (nonatomic, copy) WKWebView *webView;
31
+@end
24 32
 @implementation _SwizzleHelperWK
25 33
 -(id)inputAccessoryView
26 34
 {
27
-  return nil;
35
+    if (_webView == nil) {
36
+        return nil;
37
+    }
38
+
39
+    if ([_webView respondsToSelector:@selector(inputAssistantItem)]) {
40
+        UITextInputAssistantItem *inputAssistantItem = [_webView inputAssistantItem];
41
+        inputAssistantItem.leadingBarButtonGroups = @[];
42
+        inputAssistantItem.trailingBarButtonGroups = @[];
43
+    }
44
+    return nil;
45
+}
46
+@end
47
+#endif // !TARGET_OS_OSX
48
+
49
+#if TARGET_OS_OSX
50
+@interface RNCWKWebView : WKWebView
51
+@end
52
+@implementation RNCWKWebView
53
+- (void)scrollWheel:(NSEvent *)theEvent {
54
+  RNCWebView *rncWebView = (RNCWebView *)[self superview];
55
+  RCTAssert([rncWebView isKindOfClass:[rncWebView class]], @"superview must be an RNCWebView");
56
+  if (![rncWebView scrollEnabled]) {
57
+    [[self nextResponder] scrollWheel:theEvent];
58
+    return;
59
+  }
60
+  [super scrollWheel:theEvent];
28 61
 }
29 62
 @end
63
+#endif // TARGET_OS_OSX
64
+
65
+@interface RNCWebView () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler,
66
+#if !TARGET_OS_OSX
67
+    UIScrollViewDelegate,
68
+#endif // !TARGET_OS_OSX
69
+    RCTAutoInsetsProtocol>
30 70
 
31
-@interface RNCWebView () <WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIScrollViewDelegate, RCTAutoInsetsProtocol>
71
+@property (nonatomic, copy) RCTDirectEventBlock onFileDownload;
32 72
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingStart;
33 73
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingFinish;
34 74
 @property (nonatomic, copy) RCTDirectEventBlock onLoadingError;
@@ -38,19 +78,32 @@ static NSDictionary* customCertificatesForHost;
38 78
 @property (nonatomic, copy) RCTDirectEventBlock onMessage;
39 79
 @property (nonatomic, copy) RCTDirectEventBlock onScroll;
40 80
 @property (nonatomic, copy) RCTDirectEventBlock onContentProcessDidTerminate;
81
+#if !TARGET_OS_OSX
41 82
 @property (nonatomic, copy) WKWebView *webView;
83
+#else
84
+@property (nonatomic, copy) RNCWKWebView *webView;
85
+#endif // !TARGET_OS_OSX
86
+@property (nonatomic, strong) WKUserScript *postMessageScript;
87
+@property (nonatomic, strong) WKUserScript *atStartScript;
88
+@property (nonatomic, strong) WKUserScript *atEndScript;
42 89
 @end
43 90
 
44 91
 @implementation RNCWebView
45 92
 {
93
+#if !TARGET_OS_OSX
46 94
   UIColor * _savedBackgroundColor;
95
+#else
96
+  RCTUIColor * _savedBackgroundColor;
97
+#endif // !TARGET_OS_OSX
47 98
   BOOL _savedHideKeyboardAccessoryView;
48 99
   BOOL _savedKeyboardDisplayRequiresUserAction;
49 100
 
50 101
   // Workaround for StatusBar appearance bug for iOS 12
51 102
   // https://github.com/react-native-community/react-native-webview/issues/62
52 103
   BOOL _isFullScreenVideoOpen;
104
+#if !TARGET_OS_OSX
53 105
   UIStatusBarStyle _savedStatusBarStyle;
106
+#endif // !TARGET_OS_OSX
54 107
   BOOL _savedStatusBarHidden;
55 108
 
56 109
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
@@ -61,7 +114,11 @@ static NSDictionary* customCertificatesForHost;
61 114
 - (instancetype)initWithFrame:(CGRect)frame
62 115
 {
63 116
   if ((self = [super initWithFrame:frame])) {
117
+    #if !TARGET_OS_OSX
64 118
     super.backgroundColor = [UIColor clearColor];
119
+    #else
120
+    super.backgroundColor = [RCTUIColor clearColor];
121
+    #endif // !TARGET_OS_OSX
65 122
     _bounces = YES;
66 123
     _scrollEnabled = YES;
67 124
     _showsHorizontalScrollIndicator = YES;
@@ -71,14 +128,30 @@ static NSDictionary* customCertificatesForHost;
71 128
     _autoManageStatusBarEnabled = YES;
72 129
     _contentInset = UIEdgeInsetsZero;
73 130
     _savedKeyboardDisplayRequiresUserAction = YES;
131
+    #if !TARGET_OS_OSX
74 132
     _savedStatusBarStyle = RCTSharedApplication().statusBarStyle;
75 133
     _savedStatusBarHidden = RCTSharedApplication().statusBarHidden;
134
+    #endif // !TARGET_OS_OSX
135
+    _injectedJavaScript = nil;
136
+    _injectedJavaScriptForMainFrameOnly = YES;
137
+    _injectedJavaScriptBeforeContentLoaded = nil;
138
+    _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = YES;
76 139
 
77 140
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
78 141
     _savedContentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
79 142
 #endif
80 143
   }
81 144
 
145
+#if !TARGET_OS_OSX
146
+    [[NSNotificationCenter defaultCenter]addObserver:self
147
+    selector:@selector(appDidBecomeActive)
148
+        name:UIApplicationDidBecomeActiveNotification
149
+      object:nil];
150
+
151
+    [[NSNotificationCenter defaultCenter]addObserver:self
152
+    selector:@selector(appWillResignActive)
153
+        name:UIApplicationWillResignActiveNotification
154
+      object:nil];
82 155
   if (@available(iOS 12.0, *)) {
83 156
     // Workaround for a keyboard dismissal bug present in iOS 12
84 157
     // https://openradar.appspot.com/radar?id=5018321736957952
@@ -93,9 +166,18 @@ static NSDictionary* customCertificatesForHost;
93 166
 
94 167
     // Workaround for StatusBar appearance bug for iOS 12
95 168
     // https://github.com/react-native-community/react-native-webview/issues/62
96
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleFullScreenVideoStatusBars) name:@"_MRMediaRemotePlayerSupportedCommandsDidChangeNotification" object:nil];
97
-  }
169
+      [[NSNotificationCenter defaultCenter] addObserver:self
170
+                                               selector:@selector(showFullScreenVideoStatusBars)
171
+                                                   name:UIWindowDidBecomeVisibleNotification
172
+                                                 object:nil];
173
+
174
+      [[NSNotificationCenter defaultCenter] addObserver:self
175
+                                               selector:@selector(hideFullScreenVideoStatusBars)
176
+                                                   name:UIWindowDidBecomeHiddenNotification
177
+                                                 object:nil];
98 178
 
179
+  }
180
+#endif // !TARGET_OS_OSX
99 181
   return self;
100 182
 }
101 183
 
@@ -115,127 +197,84 @@ static NSDictionary* customCertificatesForHost;
115 197
   return nil;
116 198
 }
117 199
 
118
-- (void)didMoveToWindow
200
+- (WKWebViewConfiguration *)setUpWkWebViewConfig
119 201
 {
120
-  if (self.window != nil && _webView == nil) {
121
-    WKWebViewConfiguration *wkWebViewConfig = [WKWebViewConfiguration new];
122
-    WKPreferences *prefs = [[WKPreferences alloc]init];
123
-    if (!_javaScriptEnabled) {
124
-      prefs.javaScriptEnabled = NO;
125
-      wkWebViewConfig.preferences = prefs;
126
-    }
127
-    if (_incognito) {
128
-      wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
129
-    } else if (_cacheEnabled) {
130
-      wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
131
-    }
132
-    if(self.useSharedProcessPool) {
133
-      wkWebViewConfig.processPool = [[RNCWKProcessPoolManager sharedManager] sharedProcessPool];
134
-    }
135
-    wkWebViewConfig.userContentController = [WKUserContentController new];
136
-
137
-    if (_messagingEnabled) {
138
-      [wkWebViewConfig.userContentController addScriptMessageHandler:self name:MessageHandlerName];
139
-
140
-      NSString *source = [NSString stringWithFormat:
141
-        @"window.%@ = {"
142
-         "  postMessage: function (data) {"
143
-         "    window.webkit.messageHandlers.%@.postMessage(String(data));"
144
-         "  }"
145
-         "};", MessageHandlerName, MessageHandlerName
146
-      ];
202
+  WKWebViewConfiguration *wkWebViewConfig = [WKWebViewConfiguration new];
203
+  WKPreferences *prefs = [[WKPreferences alloc]init];
204
+  BOOL _prefsUsed = NO;
205
+  if (!_javaScriptEnabled) {
206
+    prefs.javaScriptEnabled = NO;
207
+    _prefsUsed = YES;
208
+  }
209
+  if (_allowFileAccessFromFileURLs) {
210
+    [prefs setValue:@TRUE forKey:@"allowFileAccessFromFileURLs"];
211
+    _prefsUsed = YES;
212
+  }
213
+  if (_javaScriptCanOpenWindowsAutomatically) {
214
+    [prefs setValue:@TRUE forKey:@"javaScriptCanOpenWindowsAutomatically"];
215
+    _prefsUsed = YES;
216
+  }
217
+  if (_prefsUsed) {
218
+    wkWebViewConfig.preferences = prefs;
219
+  }
220
+  if (_incognito) {
221
+    wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
222
+  } else if (_cacheEnabled) {
223
+    wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore defaultDataStore];
224
+  }
225
+  if(self.useSharedProcessPool) {
226
+    wkWebViewConfig.processPool = [[RNCWKProcessPoolManager sharedManager] sharedProcessPool];
227
+  }
228
+  wkWebViewConfig.userContentController = [WKUserContentController new];
147 229
 
148
-      WKUserScript *script = [[WKUserScript alloc] initWithSource:source injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
149
-      [wkWebViewConfig.userContentController addUserScript:script];
150
-    }
230
+  // Shim the HTML5 history API:
231
+  [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
232
+                                                            name:HistoryShimName];
233
+  [self resetupScripts:wkWebViewConfig];
151 234
 
152
-    wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
235
+#if !TARGET_OS_OSX
236
+  wkWebViewConfig.allowsInlineMediaPlayback = _allowsInlineMediaPlayback;
153 237
 #if WEBKIT_IOS_10_APIS_AVAILABLE
154
-    wkWebViewConfig.mediaTypesRequiringUserActionForPlayback = _mediaPlaybackRequiresUserAction
155
-      ? WKAudiovisualMediaTypeAll
156
-      : WKAudiovisualMediaTypeNone;
157
-    wkWebViewConfig.dataDetectorTypes = _dataDetectorTypes;
238
+  wkWebViewConfig.mediaTypesRequiringUserActionForPlayback = _mediaPlaybackRequiresUserAction
239
+    ? WKAudiovisualMediaTypeAll
240
+    : WKAudiovisualMediaTypeNone;
241
+  wkWebViewConfig.dataDetectorTypes = _dataDetectorTypes;
158 242
 #else
159
-    wkWebViewConfig.mediaPlaybackRequiresUserAction = _mediaPlaybackRequiresUserAction;
243
+  wkWebViewConfig.mediaPlaybackRequiresUserAction = _mediaPlaybackRequiresUserAction;
160 244
 #endif
245
+#endif // !TARGET_OS_OSX
161 246
 
162
-    if (_applicationNameForUserAgent) {
163
-        wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
164
-    }
165
-
166
-    if(_sharedCookiesEnabled) {
167
-      // More info to sending cookies with WKWebView
168
-      // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
169
-      if (@available(iOS 11.0, *)) {
170
-        // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
171
-        // See also https://forums.developer.apple.com/thread/97194
172
-        // check if websiteDataStore has not been initialized before
173
-        if(!_incognito && !_cacheEnabled) {
174
-          wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
175
-        }
176
-        for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
177
-          [wkWebViewConfig.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
178
-        }
179
-      } else {
180
-        NSMutableString *script = [NSMutableString string];
181
-
182
-        // Clear all existing cookies in a direct called function. This ensures that no
183
-        // javascript error will break the web content javascript.
184
-        // We keep this code here, if someone requires that Cookies are also removed within the
185
-        // the WebView and want to extends the current sharedCookiesEnabled option with an
186
-        // additional property.
187
-        // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
188
-        // for each cookie which is already available in the WebView context.
189
-        /*
190
-        [script appendString:@"(function () {\n"];
191
-        [script appendString:@"  var cookies = document.cookie.split('; ');\n"];
192
-        [script appendString:@"  for (var i = 0; i < cookies.length; i++) {\n"];
193
-        [script appendString:@"    if (cookies[i].indexOf('=') !== -1) {\n"];
194
-        [script appendString:@"      document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
195
-        [script appendString:@"    }\n"];
196
-        [script appendString:@"  }\n"];
197
-        [script appendString:@"})();\n\n"];
198
-        */
199
-
200
-        // Set cookies in a direct called function. This ensures that no
201
-        // javascript error will break the web content javascript.
202
-          // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
203
-        // for each cookie which is available in the application context.
204
-        [script appendString:@"(function () {\n"];
205
-        for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
206
-          [script appendFormat:@"document.cookie = %@ + '=' + %@",
207
-            RCTJSONStringify(cookie.name, NULL),
208
-            RCTJSONStringify(cookie.value, NULL)];
209
-          if (cookie.path) {
210
-            [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
211
-          }
212
-          if (cookie.expiresDate) {
213
-            [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
214
-              cookie.expiresDate.timeIntervalSince1970 * 1000
215
-            ];
216
-          }
217
-          [script appendString:@";\n"];
218
-        }
219
-        [script appendString:@"})();\n"];
247
+  if (_applicationNameForUserAgent) {
248
+      wkWebViewConfig.applicationNameForUserAgent = [NSString stringWithFormat:@"%@ %@", wkWebViewConfig.applicationNameForUserAgent, _applicationNameForUserAgent];
249
+  }
220 250
 
221
-        WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
222
-                                                              injectionTime:WKUserScriptInjectionTimeAtDocumentStart
223
-                                                           forMainFrameOnly:YES];
224
-        [wkWebViewConfig.userContentController addUserScript:cookieInScript];
225
-      }
226
-    }
251
+  return wkWebViewConfig;
252
+}
227 253
 
254
+- (void)didMoveToWindow
255
+{
256
+  if (self.window != nil && _webView == nil) {
257
+    WKWebViewConfiguration *wkWebViewConfig = [self setUpWkWebViewConfig];
258
+#if !TARGET_OS_OSX
228 259
     _webView = [[WKWebView alloc] initWithFrame:self.bounds configuration: wkWebViewConfig];
260
+#else
261
+    _webView = [[RNCWKWebView alloc] initWithFrame:self.bounds configuration: wkWebViewConfig];
262
+#endif // !TARGET_OS_OSX
263
+
229 264
     [self setBackgroundColor: _savedBackgroundColor];
265
+#if !TARGET_OS_OSX
230 266
     _webView.scrollView.delegate = self;
267
+#endif // !TARGET_OS_OSX
231 268
     _webView.UIDelegate = self;
232 269
     _webView.navigationDelegate = self;
270
+#if !TARGET_OS_OSX
233 271
     _webView.scrollView.scrollEnabled = _scrollEnabled;
234 272
     _webView.scrollView.pagingEnabled = _pagingEnabled;
235 273
     _webView.scrollView.bounces = _bounces;
236 274
     _webView.scrollView.showsHorizontalScrollIndicator = _showsHorizontalScrollIndicator;
237 275
     _webView.scrollView.showsVerticalScrollIndicator = _showsVerticalScrollIndicator;
238 276
     _webView.scrollView.directionalLockEnabled = _directionalLockEnabled;
277
+#endif // !TARGET_OS_OSX
239 278
     _webView.allowsLinkPreview = _allowsLinkPreview;
240 279
     [_webView addObserver:self forKeyPath:@"estimatedProgress" options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:nil];
241 280
     _webView.allowsBackForwardNavigationGestures = _allowsBackForwardNavigationGestures;
@@ -269,31 +308,42 @@ static NSDictionary* customCertificatesForHost;
269 308
         [_webView.configuration.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
270 309
         [_webView removeObserver:self forKeyPath:@"estimatedProgress"];
271 310
         [_webView removeFromSuperview];
311
+#if !TARGET_OS_OSX
272 312
         _webView.scrollView.delegate = nil;
313
+#endif // !TARGET_OS_OSX
273 314
         _webView = nil;
315
+        if (_onContentProcessDidTerminate) {
316
+          NSMutableDictionary<NSString *, id> *event = [self baseEvent];
317
+          _onContentProcessDidTerminate(event);
318
+        }
274 319
     }
275 320
 
276 321
     [super removeFromSuperview];
277 322
 }
278 323
 
279
--(void)toggleFullScreenVideoStatusBars
324
+#if !TARGET_OS_OSX
325
+-(void)showFullScreenVideoStatusBars
280 326
 {
281 327
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
282
-  if (!_autoManageStatusBarEnabled) {
283
-    return;
284
-  }
285
-  if (!_isFullScreenVideoOpen) {
328
+    if (!_autoManageStatusBarEnabled) {
329
+      return;
330
+    }
331
+
286 332
     _isFullScreenVideoOpen = YES;
287 333
     RCTUnsafeExecuteOnMainQueueSync(^{
288 334
       [RCTSharedApplication() setStatusBarStyle:UIStatusBarStyleLightContent animated:YES];
289 335
     });
290
-  } else {
336
+#pragma clang diagnostic pop
337
+}
338
+
339
+-(void)hideFullScreenVideoStatusBars
340
+{
341
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
291 342
     _isFullScreenVideoOpen = NO;
292 343
     RCTUnsafeExecuteOnMainQueueSync(^{
293 344
       [RCTSharedApplication() setStatusBarHidden:self->_savedStatusBarHidden animated:YES];
294 345
       [RCTSharedApplication() setStatusBarStyle:self->_savedStatusBarStyle animated:YES];
295 346
     });
296
-  }
297 347
 #pragma clang diagnostic pop
298 348
 }
299 349
 
@@ -323,6 +373,7 @@ static NSDictionary* customCertificatesForHost;
323 373
       }];
324 374
     }
325 375
 }
376
+#endif // !TARGET_OS_OSX
326 377
 
327 378
 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
328 379
     if ([keyPath isEqual:@"estimatedProgress"] && object == self.webView) {
@@ -336,7 +387,11 @@ static NSDictionary* customCertificatesForHost;
336 387
     }
337 388
 }
338 389
 
390
+#if !TARGET_OS_OSX
339 391
 - (void)setBackgroundColor:(UIColor *)backgroundColor
392
+#else
393
+- (void)setBackgroundColor:(RCTUIColor *)backgroundColor
394
+#endif // !TARGET_OS_OSX
340 395
 {
341 396
   _savedBackgroundColor = backgroundColor;
342 397
   if (_webView == nil) {
@@ -344,9 +399,20 @@ static NSDictionary* customCertificatesForHost;
344 399
   }
345 400
 
346 401
   CGFloat alpha = CGColorGetAlpha(backgroundColor.CGColor);
347
-  self.opaque = _webView.opaque = (alpha == 1.0);
402
+  BOOL opaque = (alpha == 1.0);
403
+#if !TARGET_OS_OSX
404
+  self.opaque = _webView.opaque = opaque;
348 405
   _webView.scrollView.backgroundColor = backgroundColor;
349 406
   _webView.backgroundColor = backgroundColor;
407
+#else
408
+  // https://stackoverflow.com/questions/40007753/macos-wkwebview-background-transparency
409
+  NSOperatingSystemVersion version = { 10, 12, 0 };
410
+  if ([[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:version]) {
411
+    [_webView setValue:@(opaque) forKey: @"drawsBackground"];
412
+  } else {
413
+    [_webView setValue:@(!opaque) forKey: @"drawsTransparentBackground"];
414
+  }
415
+#endif // !TARGET_OS_OSX
350 416
 }
351 417
 
352 418
 #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
@@ -372,10 +438,18 @@ static NSDictionary* customCertificatesForHost;
372 438
 - (void)userContentController:(WKUserContentController *)userContentController
373 439
        didReceiveScriptMessage:(WKScriptMessage *)message
374 440
 {
375
-  if (_onMessage != nil) {
376
-    NSMutableDictionary<NSString *, id> *event = [self baseEvent];
377
-    [event addEntriesFromDictionary: @{@"data": message.body}];
378
-    _onMessage(event);
441
+  if ([message.name isEqualToString:HistoryShimName]) {
442
+    if (_onLoadingFinish) {
443
+      NSMutableDictionary<NSString *, id> *event = [self baseEvent];
444
+      [event addEntriesFromDictionary: @{@"navigationType": message.body}];
445
+      _onLoadingFinish(event);
446
+    }
447
+  } else if ([message.name isEqualToString:MessageHandlerName]) {
448
+    if (_onMessage) {
449
+      NSMutableDictionary<NSString *, id> *event = [self baseEvent];
450
+      [event addEntriesFromDictionary: @{@"data": message.body}];
451
+      _onMessage(event);
452
+    }
379 453
   }
380 454
 }
381 455
 
@@ -401,6 +475,7 @@ static NSDictionary* customCertificatesForHost;
401 475
   }
402 476
 }
403 477
 
478
+#if !TARGET_OS_OSX
404 479
 - (void)setContentInset:(UIEdgeInsets)contentInset
405 480
 {
406 481
   _contentInset = contentInset;
@@ -415,6 +490,7 @@ static NSDictionary* customCertificatesForHost;
415 490
                     withScrollView:_webView.scrollView
416 491
                       updateOffset:YES];
417 492
 }
493
+#endif // !TARGET_OS_OSX
418 494
 
419 495
 - (void)visitSource
420 496
 {
@@ -451,6 +527,7 @@ static NSDictionary* customCertificatesForHost;
451 527
     }
452 528
 }
453 529
 
530
+#if !TARGET_OS_OSX
454 531
 -(void)setKeyboardDisplayRequiresUserAction:(BOOL)keyboardDisplayRequiresUserAction
455 532
 {
456 533
     if (_webView == nil) {
@@ -556,17 +633,23 @@ static NSDictionary* customCertificatesForHost;
556 633
     object_setClass(subview, newClass);
557 634
 }
558 635
 
636
+// UIScrollViewDelegate method
559 637
 - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
560 638
 {
561 639
   scrollView.decelerationRate = _decelerationRate;
562 640
 }
641
+#endif // !TARGET_OS_OSX
563 642
 
564 643
 - (void)setScrollEnabled:(BOOL)scrollEnabled
565 644
 {
566 645
   _scrollEnabled = scrollEnabled;
646
+#if !TARGET_OS_OSX
567 647
   _webView.scrollView.scrollEnabled = scrollEnabled;
648
+#endif // !TARGET_OS_OSX
568 649
 }
569 650
 
651
+#if !TARGET_OS_OSX
652
+// UIScrollViewDelegate method
570 653
 - (void)scrollViewDidScroll:(UIScrollView *)scrollView
571 654
 {
572 655
   // Don't allow scrolling the scrollView.
@@ -616,6 +699,7 @@ static NSDictionary* customCertificatesForHost;
616 699
     _showsVerticalScrollIndicator = showsVerticalScrollIndicator;
617 700
     _webView.scrollView.showsVerticalScrollIndicator = showsVerticalScrollIndicator;
618 701
 }
702
+#endif // !TARGET_OS_OSX
619 703
 
620 704
 - (void)postMessage:(NSString *)message
621 705
 {
@@ -633,7 +717,9 @@ static NSDictionary* customCertificatesForHost;
633 717
 
634 718
   // Ensure webview takes the position and dimensions of RNCWebView
635 719
   _webView.frame = self.bounds;
720
+#if !TARGET_OS_OSX
636 721
   _webView.scrollView.contentInset = _contentInset;
722
+#endif // !TARGET_OS_OSX
637 723
 }
638 724
 
639 725
 - (NSMutableDictionary<NSString *, id> *)baseEvent
@@ -695,52 +781,95 @@ static NSDictionary* customCertificatesForHost;
695 781
 #pragma mark - WKNavigationDelegate methods
696 782
 
697 783
 /**
698
-* alert
699
-*/
784
+ * alert
785
+ */
700 786
 - (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler
701 787
 {
702
-    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
703
-    [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
704
-        completionHandler();
705
-    }]];
706
-    [[self topViewController] presentViewController:alert animated:YES completion:NULL];
707
-
788
+#if !TARGET_OS_OSX
789
+  UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
790
+  [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
791
+    completionHandler();
792
+  }]];
793
+  [[self topViewController] presentViewController:alert animated:YES completion:NULL];
794
+#else
795
+  NSAlert *alert = [[NSAlert alloc] init];
796
+  [alert setMessageText:message];
797
+  [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(__unused NSModalResponse response){
798
+    completionHandler();
799
+  }];
800
+#endif // !TARGET_OS_OSX
708 801
 }
709 802
 
710 803
 /**
711
-* confirm
712
-*/
804
+ * confirm
805
+ */
713 806
 - (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
714
-    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
715
-    [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
716
-        completionHandler(YES);
717
-    }]];
718
-    [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
719
-        completionHandler(NO);
720
-    }]];
721
-    [[self topViewController] presentViewController:alert animated:YES completion:NULL];
807
+#if !TARGET_OS_OSX
808
+  UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:message preferredStyle:UIAlertControllerStyleAlert];
809
+  [alert addAction:[UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
810
+    completionHandler(YES);
811
+  }]];
812
+  [alert addAction:[UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
813
+    completionHandler(NO);
814
+  }]];
815
+  [[self topViewController] presentViewController:alert animated:YES completion:NULL];
816
+#else
817
+  NSAlert *alert = [[NSAlert alloc] init];
818
+  [alert setMessageText:message];
819
+  [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
820
+  [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
821
+  void (^callbacksHandlers)(NSModalResponse response) = ^void(NSModalResponse response) {
822
+    completionHandler(response == NSAlertFirstButtonReturn);
823
+  };
824
+  [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:callbacksHandlers];
825
+#endif // !TARGET_OS_OSX
722 826
 }
723 827
 
724 828
 /**
725
-* prompt
726
-*/
829
+ * prompt
830
+ */
727 831
 - (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString *))completionHandler{
728
-    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
729
-    [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
730
-        textField.text = defaultText;
731
-    }];
732
-    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
733
-        completionHandler([[alert.textFields lastObject] text]);
734
-    }];
735
-    [alert addAction:okAction];
736
-    UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
737
-        completionHandler(nil);
738
-    }];
739
-    [alert addAction:cancelAction];
740
-    alert.preferredAction = okAction;
741
-    [[self topViewController] presentViewController:alert animated:YES completion:NULL];
832
+#if !TARGET_OS_OSX
833
+  UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"" message:prompt preferredStyle:UIAlertControllerStyleAlert];
834
+  [alert addTextFieldWithConfigurationHandler:^(UITextField *textField) {
835
+    textField.text = defaultText;
836
+  }];
837
+  UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"Ok" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {
838
+    completionHandler([[alert.textFields lastObject] text]);
839
+  }];
840
+  [alert addAction:okAction];
841
+  UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel" style:UIAlertActionStyleCancel handler:^(UIAlertAction *action) {
842
+    completionHandler(nil);
843
+  }];
844
+  [alert addAction:cancelAction];
845
+  alert.preferredAction = okAction;
846
+  [[self topViewController] presentViewController:alert animated:YES completion:NULL];
847
+#else
848
+  NSAlert *alert = [[NSAlert alloc] init];
849
+  [alert setMessageText:prompt];
850
+
851
+  const NSRect RCTSingleTextFieldFrame = NSMakeRect(0.0, 0.0, 275.0, 22.0);
852
+  NSTextField *textField = [[NSTextField alloc] initWithFrame:RCTSingleTextFieldFrame];
853
+  textField.cell.scrollable = YES;
854
+  if (@available(macOS 10.11, *)) {
855
+    textField.maximumNumberOfLines = 1;
856
+  }
857
+  textField.stringValue = defaultText;
858
+  [alert setAccessoryView:textField];
859
+
860
+  [alert addButtonWithTitle:NSLocalizedString(@"OK", @"OK button")];
861
+  [alert addButtonWithTitle:NSLocalizedString(@"Cancel", @"Cancel button")];
862
+  [alert beginSheetModalForWindow:[NSApp keyWindow] completionHandler:^(NSModalResponse response) {
863
+    if (response == NSAlertFirstButtonReturn) {
864
+      completionHandler([textField stringValue]);
865
+    } else {
866
+      completionHandler(nil);
867
+    }
868
+  }];
869
+#endif // !TARGET_OS_OSX
742 870
 }
743 871
 
872
+#if !TARGET_OS_OSX
744 873
 /**
745 874
  * topViewController
746 875
  */
@@ -779,7 +908,7 @@ static NSDictionary* customCertificatesForHost;
779 908
   }
780 909
   return window;
781 910
 }
782
-
911
+#endif // !TARGET_OS_OSX
783 912
 
784 913
 /**
785 914
  * Decides whether to allow or cancel a navigation.
@@ -859,24 +988,42 @@ static NSDictionary* customCertificatesForHost;
859 988
   decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
860 989
                     decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
861 990
 {
991
+  WKNavigationResponsePolicy policy = WKNavigationResponsePolicyAllow;
862 992
   if (_onHttpError && navigationResponse.forMainFrame) {
863 993
     if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) {
864 994
       NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
865 995
       NSInteger statusCode = response.statusCode;
866 996
 
867 997
       if (statusCode >= 400) {
868
-        NSMutableDictionary<NSString *, id> *event = [self baseEvent];
869
-        [event addEntriesFromDictionary: @{
998
+        NSMutableDictionary<NSString *, id> *httpErrorEvent = [self baseEvent];
999
+        [httpErrorEvent addEntriesFromDictionary: @{
870 1000
           @"url": response.URL.absoluteString,
871 1001
           @"statusCode": @(statusCode)
872 1002
         }];
873 1003
 
874
-        _onHttpError(event);
1004
+        _onHttpError(httpErrorEvent);
1005
+      }
1006
+
1007
+      NSString *disposition = nil;
1008
+      if (@available(iOS 13, *)) {
1009
+        disposition = [response valueForHTTPHeaderField:@"Content-Disposition"];
1010
+      }
1011
+      BOOL isAttachment = disposition != nil && [disposition hasPrefix:@"attachment"];
1012
+      if (isAttachment || !navigationResponse.canShowMIMEType) {
1013
+        if (_onFileDownload) {
1014
+          policy = WKNavigationResponsePolicyCancel;
1015
+
1016
+          NSMutableDictionary<NSString *, id> *downloadEvent = [self baseEvent];
1017
+          [downloadEvent addEntriesFromDictionary: @{
1018
+            @"downloadUrl": (response.URL).absoluteString,
1019
+          }];
1020
+          _onFileDownload(downloadEvent);
1021
+        }
875 1022
       }
876 1023
     }
877 1024
   }
878 1025
 
879
-  decisionHandler(WKNavigationResponsePolicyAllow);
1026
+  decisionHandler(policy);
880 1027
 }
881 1028
 
882 1029
 /**
@@ -896,7 +1043,7 @@ static NSDictionary* customCertificatesForHost;
896 1043
       return;
897 1044
     }
898 1045
 
899
-    if ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102) {
1046
+    if ([error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 102 || [error.domain isEqualToString:@"WebKitErrorDomain"] && error.code == 101) {
900 1047
       // Error code 102 "Frame load interrupted" is raised by the WKWebView
901 1048
       // when the URL is from an http redirect. This is a common pattern when
902 1049
       // implementing OAuth with a WebView.
@@ -927,23 +1074,49 @@ static NSDictionary* customCertificatesForHost;
927 1074
   }];
928 1075
 }
929 1076
 
1077
+-(void)forceIgnoreSilentHardwareSwitch:(BOOL)initialSetup
1078
+{
1079
+    NSString *mp3Str = @"data:audio/mp3;base64,//tAxAAAAAAAAAAAAAAAAAAAAAAASW5mbwAAAA8AAAAFAAAESAAzMzMzMzMzMzMzMzMzMzMzMzMzZmZmZmZmZmZmZmZmZmZmZmZmZmaZmZmZmZmZmZmZmZmZmZmZmZmZmczMzMzMzMzMzMzMzMzMzMzMzMzM//////////////////////////8AAAA5TEFNRTMuMTAwAZYAAAAAAAAAABQ4JAMGQgAAOAAABEhNIZS0AAAAAAD/+0DEAAPH3Yz0AAR8CPqyIEABp6AxjG/4x/XiInE4lfQDFwIIRE+uBgZoW4RL0OLMDFn6E5v+/u5ehf76bu7/6bu5+gAiIQGAABQIUJ0QolFghEn/9PhZQpcUTpXMjo0OGzRCZXyKxoIQzB2KhCtGobpT9TRVj/3Pmfp+f8X7Pu1B04sTnc3s0XhOlXoGVCMNo9X//9/r6a10TZEY5DsxqvO7mO5qFvpFCmKIjhpSItGsUYcRO//7QsQRgEiljQIAgLFJAbIhNBCa+JmorCbOi5q9nVd2dKnusTMQg4MFUlD6DQ4OFijwGAijRMfLbHG4nLVTjydyPlJTj8pfPflf9/5GD950A5e+jsrmNZSjSirjs1R7hnkia8vr//l/7Nb+crvr9Ok5ZJOylUKRxf/P9Zn0j2P4pJYXyKkeuy5wUYtdmOu6uobEtFqhIJViLEKIjGxchGev/L3Y0O3bwrIOszTBAZ7Ih28EUaSOZf/7QsQfg8fpjQIADN0JHbGgQBAZ8T//y//t/7d/2+f5m7MdCeo/9tdkMtGLbt1tqnabRroO1Qfvh20yEbei8nfDXP7btW7f9/uO9tbe5IvHQbLlxpf3DkAk0ojYcv///5/u3/7PTfGjPEPUvt5D6f+/3Lea4lz4tc4TnM/mFPrmalWbboeNiNyeyr+vufttZuvrVrt/WYv3T74JFo8qEDiJqJrmDTs///v99xDku2xG02jjunrICP/7QsQtA8kpkQAAgNMA/7FgQAGnobgfghgqA+uXwWQ3XFmGimSbe2X3ksY//KzK1a2k6cnNWOPJnPWUsYbKqkh8RJzrVf///P///////4vyhLKHLrCb5nIrYIUss4cthigL1lQ1wwNAc6C1pf1TIKRSkt+a//z+yLVcwlXKSqeSuCVQFLng2h4AFAFgTkH+Z/8jTX/zr//zsJV/5f//5UX/0ZNCNCCaf5lTCTRkaEdhNP//n/KUjf/7QsQ5AEhdiwAAjN7I6jGddBCO+WGTQ1mXrYatSAgaykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqg==";
1080
+    NSString *scr;
1081
+    if (initialSetup) {
1082
+        scr = [NSString stringWithFormat:@"var s=new Audio('%@');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s);true", mp3Str];
1083
+    } else {
1084
+        scr = [NSString stringWithFormat:@"var s=document.getElementById('wkwebviewAudio');s.src=null;s.parentNode.removeChild(s);s=null;s=new Audio('%@');s.id='wkwebviewAudio';s.controls=false;s.loop=true;s.play();document.body.appendChild(s);true", mp3Str];
1085
+    }
1086
+    [self evaluateJS: scr thenCall: nil];
1087
+}
1088
+
1089
+-(void)disableIgnoreSilentSwitch
1090
+{
1091
+    [self evaluateJS: @"document.getElementById('wkwebviewAudio').src=null;true" thenCall: nil];
1092
+}
1093
+
1094
+-(void)appDidBecomeActive
1095
+{
1096
+    if (_ignoreSilentHardwareSwitch) {
1097
+      [self forceIgnoreSilentHardwareSwitch:false];
1098
+    }
1099
+}
1100
+
1101
+-(void)appWillResignActive
1102
+{
1103
+  if (_ignoreSilentHardwareSwitch) {
1104
+    [self disableIgnoreSilentSwitch];
1105
+  }
1106
+}
1107
+
930 1108
 /**
931 1109
  * Called when the navigation is complete.
932 1110
  * @see https://fburl.com/rtys6jlb
933 1111
  */
934
-- (void)      webView:(WKWebView *)webView
1112
+- (void)webView:(WKWebView *)webView
935 1113
   didFinishNavigation:(WKNavigation *)navigation
936 1114
 {
937
-  if (_injectedJavaScript) {
938
-    [self evaluateJS: _injectedJavaScript thenCall: ^(NSString *jsEvaluationValue) {
939
-      NSMutableDictionary *event = [self baseEvent];
940
-      event[@"jsEvaluationValue"] = jsEvaluationValue;
1115
+  if (_ignoreSilentHardwareSwitch) {
1116
+    [self forceIgnoreSilentHardwareSwitch:true];
1117
+  }
941 1118
 
942
-      if (self.onLoadingFinish) {
943
-        self.onLoadingFinish(event);
944
-      }
945
-    }];
946
-  } else if (_onLoadingFinish) {
1119
+  if (_onLoadingFinish) {
947 1120
     _onLoadingFinish([self baseEvent]);
948 1121
   }
949 1122
 }
@@ -984,11 +1157,181 @@ static NSDictionary* customCertificatesForHost;
984 1157
   [_webView stopLoading];
985 1158
 }
986 1159
 
1160
+#if !TARGET_OS_OSX
987 1161
 - (void)setBounces:(BOOL)bounces
988 1162
 {
989 1163
   _bounces = bounces;
990 1164
   _webView.scrollView.bounces = bounces;
991 1165
 }
1166
+#endif // !TARGET_OS_OSX
1167
+
1168
+
1169
+- (void)setInjectedJavaScript:(NSString *)source {
1170
+  _injectedJavaScript = source;
1171
+
1172
+  self.atEndScript = source == nil ? nil : [[WKUserScript alloc] initWithSource:source
1173
+      injectionTime:WKUserScriptInjectionTimeAtDocumentEnd
1174
+    forMainFrameOnly:_injectedJavaScriptForMainFrameOnly];
1175
+
1176
+  if(_webView != nil){
1177
+    [self resetupScripts:_webView.configuration];
1178
+  }
1179
+}
1180
+
1181
+- (void)setInjectedJavaScriptBeforeContentLoaded:(NSString *)source {
1182
+  _injectedJavaScriptBeforeContentLoaded = source;
1183
+
1184
+  self.atStartScript = source == nil ? nil : [[WKUserScript alloc] initWithSource:source
1185
+       injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1186
+    forMainFrameOnly:_injectedJavaScriptBeforeContentLoadedForMainFrameOnly];
1187
+
1188
+  if(_webView != nil){
1189
+    [self resetupScripts:_webView.configuration];
1190
+  }
1191
+}
1192
+
1193
+- (void)setInjectedJavaScriptForMainFrameOnly:(BOOL)mainFrameOnly {
1194
+  _injectedJavaScriptForMainFrameOnly = mainFrameOnly;
1195
+  [self setInjectedJavaScript:_injectedJavaScript];
1196
+}
1197
+
1198
+- (void)setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly:(BOOL)mainFrameOnly {
1199
+  _injectedJavaScriptBeforeContentLoadedForMainFrameOnly = mainFrameOnly;
1200
+  [self setInjectedJavaScriptBeforeContentLoaded:_injectedJavaScriptBeforeContentLoaded];
1201
+}
1202
+
1203
+- (void)setMessagingEnabled:(BOOL)messagingEnabled {
1204
+  _messagingEnabled = messagingEnabled;
1205
+
1206
+  self.postMessageScript = _messagingEnabled ?
1207
+  [
1208
+   [WKUserScript alloc]
1209
+   initWithSource: [
1210
+                    NSString
1211
+                    stringWithFormat:
1212
+                    @"window.%@ = {"
1213
+                    "  postMessage: function (data) {"
1214
+                    "    window.webkit.messageHandlers.%@.postMessage(String(data));"
1215
+                    "  }"
1216
+                    "};", MessageHandlerName, MessageHandlerName
1217
+                    ]
1218
+   injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1219
+   /* TODO: For a separate (minor) PR: use logic like this (as react-native-wkwebview does) so that messaging can be used in all frames if desired.
1220
+    *       I am keeping it as YES for consistency with previous behaviour. */
1221
+   // forMainFrameOnly:_messagingEnabledForMainFrameOnly
1222
+   forMainFrameOnly:YES
1223
+   ] :
1224
+  nil;
1225
+
1226
+  if(_webView != nil){
1227
+    [self resetupScripts:_webView.configuration];
1228
+  }
1229
+}
1230
+
1231
+- (void)resetupScripts:(WKWebViewConfiguration *)wkWebViewConfig {
1232
+  [wkWebViewConfig.userContentController removeAllUserScripts];
1233
+  [wkWebViewConfig.userContentController removeScriptMessageHandlerForName:MessageHandlerName];
1234
+
1235
+  NSString *html5HistoryAPIShimSource = [NSString stringWithFormat:
1236
+    @"(function(history) {\n"
1237
+    "  function notify(type) {\n"
1238
+    "    setTimeout(function() {\n"
1239
+    "      window.webkit.messageHandlers.%@.postMessage(type)\n"
1240
+    "    }, 0)\n"
1241
+    "  }\n"
1242
+    "  function shim(f) {\n"
1243
+    "    return function pushState() {\n"
1244
+    "      notify('other')\n"
1245
+    "      return f.apply(history, arguments)\n"
1246
+    "    }\n"
1247
+    "  }\n"
1248
+    "  history.pushState = shim(history.pushState)\n"
1249
+    "  history.replaceState = shim(history.replaceState)\n"
1250
+    "  window.addEventListener('popstate', function() {\n"
1251
+    "    notify('backforward')\n"
1252
+    "  })\n"
1253
+    "})(window.history)\n", HistoryShimName
1254
+  ];
1255
+  WKUserScript *script = [[WKUserScript alloc] initWithSource:html5HistoryAPIShimSource injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
1256
+  [wkWebViewConfig.userContentController addUserScript:script];
1257
+
1258
+  if(_sharedCookiesEnabled) {
1259
+    // More info to sending cookies with WKWebView
1260
+    // https://stackoverflow.com/questions/26573137/can-i-set-the-cookies-to-be-used-by-a-wkwebview/26577303#26577303
1261
+    if (@available(iOS 11.0, *)) {
1262
+      // Set Cookies in iOS 11 and above, initialize websiteDataStore before setting cookies
1263
+      // See also https://forums.developer.apple.com/thread/97194
1264
+      // check if websiteDataStore has not been initialized before
1265
+      if(!_incognito && !_cacheEnabled) {
1266
+        wkWebViewConfig.websiteDataStore = [WKWebsiteDataStore nonPersistentDataStore];
1267
+      }
1268
+      for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
1269
+        [wkWebViewConfig.websiteDataStore.httpCookieStore setCookie:cookie completionHandler:nil];
1270
+      }
1271
+    } else {
1272
+      NSMutableString *script = [NSMutableString string];
1273
+
1274
+      // Clear all existing cookies in a direct called function. This ensures that no
1275
+      // javascript error will break the web content javascript.
1276
+      // We keep this code here, if someone requires that Cookies are also removed within the
1277
+      // the WebView and want to extends the current sharedCookiesEnabled option with an
1278
+      // additional property.
1279
+      // Generates JS: document.cookie = "key=; Expires=Thu, 01 Jan 1970 00:00:01 GMT;"
1280
+      // for each cookie which is already available in the WebView context.
1281
+      /*
1282
+      [script appendString:@"(function () {\n"];
1283
+      [script appendString:@"  var cookies = document.cookie.split('; ');\n"];
1284
+      [script appendString:@"  for (var i = 0; i < cookies.length; i++) {\n"];
1285
+      [script appendString:@"    if (cookies[i].indexOf('=') !== -1) {\n"];
1286
+      [script appendString:@"      document.cookie = cookies[i].split('=')[0] + '=; Expires=Thu, 01 Jan 1970 00:00:01 GMT';\n"];
1287
+      [script appendString:@"    }\n"];
1288
+      [script appendString:@"  }\n"];
1289
+      [script appendString:@"})();\n\n"];
1290
+      */
1291
+
1292
+      // Set cookies in a direct called function. This ensures that no
1293
+      // javascript error will break the web content javascript.
1294
+        // Generates JS: document.cookie = "key=value; Path=/; Expires=Thu, 01 Jan 20xx 00:00:01 GMT;"
1295
+      // for each cookie which is available in the application context.
1296
+      [script appendString:@"(function () {\n"];
1297
+      for (NSHTTPCookie *cookie in [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]) {
1298
+        [script appendFormat:@"document.cookie = %@ + '=' + %@",
1299
+          RCTJSONStringify(cookie.name, NULL),
1300
+          RCTJSONStringify(cookie.value, NULL)];
1301
+        if (cookie.path) {
1302
+          [script appendFormat:@" + '; Path=' + %@", RCTJSONStringify(cookie.path, NULL)];
1303
+        }
1304
+        if (cookie.expiresDate) {
1305
+          [script appendFormat:@" + '; Expires=' + new Date(%f).toUTCString()",
1306
+            cookie.expiresDate.timeIntervalSince1970 * 1000
1307
+          ];
1308
+        }
1309
+        [script appendString:@";\n"];
1310
+      }
1311
+      [script appendString:@"})();\n"];
1312
+
1313
+      WKUserScript* cookieInScript = [[WKUserScript alloc] initWithSource:script
1314
+                                                            injectionTime:WKUserScriptInjectionTimeAtDocumentStart
1315
+                                                         forMainFrameOnly:YES];
1316
+      [wkWebViewConfig.userContentController addUserScript:cookieInScript];
1317
+    }
1318
+  }
1319
+
1320
+  if(_messagingEnabled){
1321
+    if (self.postMessageScript){
1322
+      [wkWebViewConfig.userContentController addScriptMessageHandler:[[RNCWeakScriptMessageDelegate alloc] initWithDelegate:self]
1323
+                                                                       name:MessageHandlerName];
1324
+      [wkWebViewConfig.userContentController addUserScript:self.postMessageScript];
1325
+    }
1326
+    if (self.atEndScript) {
1327
+      [wkWebViewConfig.userContentController addUserScript:self.atEndScript];
1328
+    }
1329
+  }
1330
+  // Whether or not messaging is enabled, add the startup script if it exists.
1331
+  if (self.atStartScript) {
1332
+    [wkWebViewConfig.userContentController addUserScript:self.atStartScript];
1333
+  }
1334
+}
992 1335
 
993 1336
 - (NSURLRequest *)requestForSource:(id)json {
994 1337
   NSURLRequest *request = [RCTConvert NSURLRequest:self.source];
@@ -1011,3 +1354,19 @@ static NSDictionary* customCertificatesForHost;
1011 1354
 }
1012 1355
 
1013 1356
 @end
1357
+
1358
+@implementation RNCWeakScriptMessageDelegate
1359
+
1360
+- (instancetype)initWithDelegate:(id<WKScriptMessageHandler>)scriptDelegate {
1361
+    self = [super init];
1362
+    if (self) {
1363
+        _scriptDelegate = scriptDelegate;
1364
+    }
1365
+    return self;
1366
+}
1367
+
1368
+- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
1369
+    [self.scriptDelegate userContentController:userContentController didReceiveScriptMessage:message];
1370
+}
1371
+
1372
+@end

ios/RNCWebViewManager.h → apple/RNCWebViewManager.h 查看文件


ios/RNCWebViewManager.m → apple/RNCWebViewManager.m 查看文件

@@ -14,19 +14,6 @@
14 14
 @interface RNCWebViewManager () <RNCWebViewDelegate>
15 15
 @end
16 16
 
17
-@implementation RCTConvert (UIScrollView)
18
-
19
-#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 110000 /* __IPHONE_11_0 */
20
-RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
21
-                                                                  @"automatic": @(UIScrollViewContentInsetAdjustmentAutomatic),
22
-                                                                  @"scrollableAxes": @(UIScrollViewContentInsetAdjustmentScrollableAxes),
23
-                                                                  @"never": @(UIScrollViewContentInsetAdjustmentNever),
24
-                                                                  @"always": @(UIScrollViewContentInsetAdjustmentAlways),
25
-                                                                  }), UIScrollViewContentInsetAdjustmentNever, integerValue)
26
-#endif
27
-
28
-@end
29
-
30 17
 @implementation RNCWebViewManager
31 18
 {
32 19
   NSConditionLock *_shouldStartLoadLock;
@@ -35,7 +22,11 @@ RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
35 22
 
36 23
 RCT_EXPORT_MODULE()
37 24
 
25
+#if !TARGET_OS_OSX
38 26
 - (UIView *)view
27
+#else
28
+- (RCTUIView *)view
29
+#endif // !TARGET_OS_OSX
39 30
 {
40 31
   RNCWebView *webView = [RNCWebView new];
41 32
   webView.delegate = self;
@@ -43,6 +34,7 @@ RCT_EXPORT_MODULE()
43 34
 }
44 35
 
45 36
 RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
37
+RCT_EXPORT_VIEW_PROPERTY(onFileDownload, RCTDirectEventBlock)
46 38
 RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
47 39
 RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
48 40
 RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
@@ -51,7 +43,12 @@ RCT_EXPORT_VIEW_PROPERTY(onHttpError, RCTDirectEventBlock)
51 43
 RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
52 44
 RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
53 45
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
46
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
47
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptForMainFrameOnly, BOOL)
48
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoadedForMainFrameOnly, BOOL)
54 49
 RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
50
+RCT_EXPORT_VIEW_PROPERTY(javaScriptCanOpenWindowsAutomatically, BOOL)
51
+RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
55 52
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
56 53
 RCT_EXPORT_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, BOOL)
57 54
 #if WEBKIT_IOS_10_APIS_AVAILABLE
@@ -109,9 +106,11 @@ RCT_CUSTOM_VIEW_PROPERTY(sharedCookiesEnabled, BOOL, RNCWebView) {
109 106
     view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json];
110 107
 }
111 108
 
109
+#if !TARGET_OS_OSX
112 110
 RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWebView) {
113 111
   view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json];
114 112
 }
113
+#endif // !TARGET_OS_OSX
115 114
 
116 115
 RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWebView) {
117 116
     view.directionalLockEnabled = json == nil ? true : [RCTConvert BOOL: json];

+ 1
- 0
babel.config.js 查看文件

@@ -6,5 +6,6 @@ module.exports = function(api) {
6 6
         presets: ['module:metro-react-native-babel-preset'],
7 7
       },
8 8
     },
9
+    presets: ['module:metro-react-native-babel-preset'],
9 10
   };
10 11
 };

+ 1
- 1
bin/setup 查看文件

@@ -18,7 +18,7 @@ fi
18 18
 # React Native installed?
19 19
 if ! [ -x "$(command -v react-native)" ]; then
20 20
   echo 'Error: React Native is not installed.' >&2
21
-  echo 'Go here: https://facebook.github.io/react-native/docs/getting-started.html' >&2
21
+  echo 'Go here: https://reactnative.dev/docs/getting-started.html' >&2
22 22
   echo 'Use the "Building Projects With Native Code" option.'
23 23
   exit 1
24 24
 fi

+ 46
- 0
docs/Contributing.md 查看文件

@@ -8,6 +8,52 @@ Secondly, we'd like the contribution experience to be as good as possible. While
8 8
 
9 9
 After you fork the repo, clone it to your machine, and make your changes, you'll want to test them in an app.
10 10
 
11
+There are two methods of testing:
12
+1) Testing within a clone of react-native-webview
13
+2) Testing in a new `react-native init` project
14
+
15
+### Testing within react-native-webview
16
+
17
+#### For all platforms:
18
+```
19
+$ yarn install
20
+```
21
+
22
+#### For Android:
23
+```
24
+$ yarn start:android
25
+```
26
+
27
+The Android example app will built, the Metro Bundler will launch, and the example app will be installed and started in the Android emulator.
28
+
29
+#### For iOS:
30
+```
31
+$ cd example/ios
32
+$ pod install
33
+$ cd ../..
34
+$ yarn start:ios
35
+```
36
+
37
+The iOS example app will be built, the Metro bundler will launch, and the example app will be install and started in the Simulator.
38
+
39
+#### for macOS:
40
+```
41
+$ open example/macos/example.xcodeproj
42
+$ yarn start:macos
43
+```
44
+
45
+The Metro Bundler will now be running in the Terminal for react-native-macos.  In XCode select the `example-macos` target and Run.
46
+
47
+#### For Windows:
48
+```
49
+$ yarn start:windows
50
+$ open example/windows/WebViewWindows.sln and click run button.
51
+```
52
+
53
+The Metro Bundler will now be running in the Terminal for react-native-windows and the example app will be install and started
54
+
55
+### Testing in a new `react-native init` project
56
+
11 57
 In a new `react-native init` project, do this:
12 58
 
13 59
 ```

+ 14
- 14
docs/Custom-Android.md 查看文件

@@ -1,14 +1,14 @@
1 1
 While the built-in web view has a lot of features, it is not possible to handle every use-case in React Native. You can, however, extend the web view with native code without forking React Native or duplicating all the existing web view code.
2 2
 
3
-Before you do this, you should be familiar with the concepts in [native UI components](https://facebook.github.io/react-native/docs/native-components-android). You should also familiarise yourself with the [native code for web views](https://github.com/facebook/react-native/blob/master/ReactAndroid/src/main/java/com/facebook/react/views/webview/ReactWebViewManager.java), as you will have to use this as a reference when implementing new features—although a deep understanding is not required.
3
+Before you do this, you should be familiar with the concepts in [native UI components](https://reactnative.dev/docs/native-components-android). You should also familiarise yourself with the [native code for web views](https://github.com/react-native-community/react-native-webview/blob/master/android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java), as you will have to use this as a reference when implementing new features—although a deep understanding is not required.
4 4
 
5 5
 ## Native Code
6 6
 
7 7
 To get started, you'll need to create a subclass of `RNCWebViewManager`, `RNCWebView`, and `ReactWebViewClient`. In your view manager, you'll then need to override:
8 8
 
9
-* `createReactWebViewInstance`
10
-* `getName`
11
-* `addEventEmitters`
9
+- `createReactWebViewInstance`
10
+- `getName`
11
+- `addEventEmitters`
12 12
 
13 13
 ```java
14 14
 @ReactModule(name = CustomWebViewManager.REACT_CLASS)
@@ -168,22 +168,22 @@ public class CustomWebViewManager extends RNCWebViewManager {
168 168
 
169 169
 To use your custom web view, you'll need to create a class for it. Your class must:
170 170
 
171
-* Export all the prop types from `WebView.propTypes`
172
-* Return a `WebView` component with the prop `nativeConfig.component` set to your native component (see below)
171
+- Export all the prop types from `WebView.propTypes`
172
+- Return a `WebView` component with the prop `nativeConfig.component` set to your native component (see below)
173 173
 
174 174
 To get your native component, you must use `requireNativeComponent`: the same as for regular custom components. However, you must pass in an extra third argument, `WebView.extraNativeComponentConfig`. This third argument contains prop types that are only required for native code.
175 175
 
176 176
 ```javascript
177
-import React, {Component, PropTypes} from 'react';
178
-import {requireNativeComponent} from 'react-native';
179
-import {WebView} from 'react-native-webview';
177
+import React, { Component, PropTypes } from 'react';
178
+import { requireNativeComponent } from 'react-native';
179
+import { WebView } from 'react-native-webview';
180 180
 
181 181
 export default class CustomWebView extends Component {
182 182
   static propTypes = WebView.propTypes;
183 183
 
184 184
   render() {
185 185
     return (
186
-      <WebView {...this.props} nativeConfig={{component: RCTCustomWebView}} />
186
+      <WebView {...this.props} nativeConfig={{ component: RCTCustomWebView }} />
187 187
     );
188 188
   }
189 189
 }
@@ -191,7 +191,7 @@ export default class CustomWebView extends Component {
191 191
 const RCTCustomWebView = requireNativeComponent(
192 192
   'RCTCustomWebView',
193 193
   CustomWebView,
194
-  WebView.extraNativeComponentConfig
194
+  WebView.extraNativeComponentConfig,
195 195
 );
196 196
 ```
197 197
 
@@ -213,8 +213,8 @@ export default class CustomWebView extends Component {
213 213
     finalUrl: 'about:blank',
214 214
   };
215 215
 
216
-  _onNavigationCompleted = (event) => {
217
-    const {onNavigationCompleted} = this.props;
216
+  _onNavigationCompleted = event => {
217
+    const { onNavigationCompleted } = this.props;
218 218
     onNavigationCompleted && onNavigationCompleted(event);
219 219
   };
220 220
 
@@ -249,6 +249,6 @@ const RCTCustomWebView = requireNativeComponent(
249 249
       ...WebView.extraNativeComponentConfig.nativeOnly,
250 250
       onScrollToBottom: true,
251 251
     },
252
-  }
252
+  },
253 253
 );
254 254
 ```

+ 45
- 11
docs/Getting-Started.md 查看文件

@@ -2,19 +2,21 @@
2 2
 
3 3
 Here's how to get started quickly with the React Native WebView.
4 4
 
5
-#### 1. Add react-native-webview to your dependencies
5
+## 1. Add react-native-webview to your dependencies
6 6
 
7 7
 ```
8 8
 $ yarn add react-native-webview
9 9
 ```
10
- (or)
11
- 
12
- For npm use
10
+
11
+(or)
12
+
13
+For npm use
14
+
13 15
 ```
14 16
 $ npm install --save react-native-webview
15 17
 ```
16 18
 
17
-#### 2. Link native dependencies
19
+## 2. Link native dependencies
18 20
 
19 21
 From react-native 0.60 autolinking will take care of the link step but don't forget to run `pod install`
20 22
 
@@ -24,13 +26,21 @@ React Native modules that include native Objective-C, Swift, Java, or Kotlin cod
24 26
 $ react-native link react-native-webview
25 27
 ```
26 28
 
27
-iOS:
29
+_NOTE: If you ever need to uninstall React Native WebView, run `react-native unlink react-native-webview` to unlink it._
30
+
31
+### iOS & macOS:
32
+
33
+If using CocoaPods, in the `ios/` or `macos/` directory run:
28 34
 
29
-If using cocoapods in the `ios/` directory run
30 35
 ```
31 36
 $ pod install
32 37
 ```
33 38
 
39
+While you can manually link the old way using [react-native own tutorial](https://reactnative.dev/docs/linking-libraries-ios), we find it easier to use CocoaPods.
40
+If you wish to use CocoaPods and haven't set it up yet, please instead refer to [that article](https://engineering.brigad.co/demystifying-react-native-modules-linking-ae6c017a6b4a).
41
+
42
+### Android:
43
+
34 44
 Android - react-native-webview version <6:
35 45
 This module does not require any extra step after running the link command 🎉
36 46
 
@@ -44,12 +54,36 @@ android.enableJetifier=true
44 54
 
45 55
 For Android manual installation, please refer to [this article](https://engineering.brigad.co/demystifying-react-native-modules-linking-964399ec731b) where you can find detailed step on how to link any react-native project.
46 56
 
47
-For iOS, while you can manually link the old way using [react-native own tutorial](https://facebook.github.io/react-native/docs/linking-libraries-ios), we find it easier to use cocoapods.
48
-If you wish to use cocoapods and haven't set it up yet, please instead refer to [that article](https://engineering.brigad.co/demystifying-react-native-modules-linking-ae6c017a6b4a).
57
+### Windows:
49 58
 
50
-_NOTE: If you ever need to uninstall React Native WebView, run `react-native unlink react-native-webview` to unlink it._
59
+Autolinking is not yet supported for ReactNativeWindows. Make following additions to the given files manually:
60
+
61
+#### **windows/myapp.sln**
62
+
63
+Add the `ReactNativeWebView` project to your solution.
64
+
65
+1. Open the solution in Visual Studio 2019
66
+2. Right-click Solution icon in Solution Explorer > Add > Existing Project
67
+   Select `node_modules\react-native-webview\windows\ReactNativeWebView\ReactNativeWebView.vcxproj`
68
+
69
+#### **windows/myapp/myapp.vcxproj**
70
+
71
+Add a reference to `ReactNativeWebView` to your main application project. From Visual Studio 2019:
72
+
73
+1. Right-click main application project > Add > Reference...
74
+   Check `ReactNativeWebView` from Solution Projects.
75
+
76
+2. Modify files below to add the package providers to your main application project
77
+
78
+#### **pch.h**
79
+
80
+Add `#include "winrt/ReactNativeWebView.h"`.
81
+
82
+#### **app.cpp**
83
+
84
+Add `PackageProviders().Append(winrt::ReactNativeWebView::ReactPackageProvider());` before `InitializeComponent();`.
51 85
 
52
-#### 3. Import the webview into your component
86
+## 3. Import the webview into your component
53 87
 
54 88
 ```js
55 89
 import React, { Component } from 'react';

+ 202
- 47
docs/Guide.md 查看文件

@@ -8,11 +8,13 @@ _This guide is currently a work in progress._
8 8
 
9 9
 - [Basic Inline HTML](Guide.md#basic-inline-html)
10 10
 - [Basic URL Source](Guide.md#basic-url-source)
11
+- [Loading local HTML files](Guide.md#loading-local-html-files)
11 12
 - [Controlling navigation state changes](Guide.md#controlling-navigation-state-changes)
12 13
 - [Add support for File Upload](Guide.md#add-support-for-file-upload)
13 14
 - [Multiple files upload](Guide.md#multiple-files-upload)
14 15
 - [Add support for File Download](Guide.md#add-support-for-file-download)
15 16
 - [Communicating between JS and Native](Guide.md#communicating-between-js-and-native)
17
+- [Working with custom headers, sessions, and cookies](Guide.md#working-with-custom-headers-sessions-and-cookies)
16 18
 
17 19
 ### Basic inline HTML
18 20
 
@@ -44,14 +46,49 @@ This is the most common use-case for WebView.
44 46
 import React, { Component } from 'react';
45 47
 import { WebView } from 'react-native-webview';
46 48
 
49
+class MyWeb extends Component {
50
+  render() {
51
+    return <WebView source={{ uri: 'https://reactnative.dev/' }} />;
52
+  }
53
+}
54
+```
55
+
56
+### Loading local HTML files
57
+
58
+Note: This is currently not working as discussed in [#428](https://github.com/react-native-community/react-native-webview/issues/428) and [#518](https://github.com/react-native-community/react-native-webview/issues/518). Possible workarounds include bundling all assets with webpack or similar, or running a [local webserver](https://github.com/futurepress/react-native-static-server).
59
+
60
+<details><summary>Show non-working method</summary>
61
+
62
+Sometimes you would have bundled an HTML file along with the app and would like to load the HTML asset into your WebView. To do this on iOS and Windows, you can just import the html file like any other asset as shown below.
63
+
64
+```js
65
+import React, { Component } from 'react';
66
+import { WebView } from 'react-native-webview';
67
+
68
+const myHtmlFile = require('./my-asset-folder/local-site.html');
69
+
70
+class MyWeb extends Component {
71
+  render() {
72
+    return <WebView source={myHtmlFile} />;
73
+  }
74
+}
75
+```
76
+
77
+However on Android, you need to place the HTML file inside your android project's asset directory. For example, if `local-site.html` is your HTML file and you'd like to load it into the webview, you should move the file to your project's android asset directory which is `your-project/android/app/src/main/assets/`. Then you can load the html file as shown in the following code block
78
+
79
+```js
80
+import React, { Component } from 'react';
81
+import { WebView } from 'react-native-webview';
82
+
47 83
 class MyWeb extends Component {
48 84
   render() {
49 85
     return (
50
-      <WebView source={{ uri: 'https://facebook.github.io/react-native/' }} />
86
+      <WebView source={{ uri: 'file:///android_asset/local-site.html' }} />
51 87
     );
52 88
   }
53 89
 }
54 90
 ```
91
+</details>
55 92
 
56 93
 ### Controlling navigation state changes
57 94
 
@@ -68,7 +105,7 @@ class MyWeb extends Component {
68 105
     return (
69 106
       <WebView
70 107
         ref={ref => (this.webview = ref)}
71
-        source={{ uri: 'https://facebook.github.io/react-native/' }}
108
+        source={{ uri: 'https://reactnative.dev/' }}
72 109
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
73 110
       />
74 111
     );
@@ -105,7 +142,7 @@ class MyWeb extends Component {
105 142
 
106 143
     // redirect somewhere else
107 144
     if (url.includes('google.com')) {
108
-      const newURL = 'https://facebook.github.io/react-native/';
145
+      const newURL = 'https://reactnative.dev/';
109 146
       const redirectTo = 'window.location = "' + newURL + '"';
110 147
       this.webview.injectJavaScript(redirectTo);
111 148
     }
@@ -113,44 +150,6 @@ class MyWeb extends Component {
113 150
 }
114 151
 ```
115 152
 
116
-#### Intercepting hash URL changes
117
-
118
-While `onNavigationStateChange` will trigger on URL changes, it does not trigger when only the hash URL ("anchor") changes, e.g. from `https://example.com/users#list` to `https://example.com/users#help`.
119
-
120
-You can inject some JavaScript to wrap the history functions in order to intercept these hash URL changes.
121
-
122
-```jsx
123
-<WebView
124
-  source={{ uri: someURI }}
125
-  injectedJavaScript={`
126
-    (function() {
127
-      function wrap(fn) {
128
-        return function wrapper() {
129
-          var res = fn.apply(this, arguments);
130
-          window.ReactNativeWebView.postMessage('navigationStateChange');
131
-          return res;
132
-        }
133
-      }
134
-
135
-      history.pushState = wrap(history.pushState);
136
-      history.replaceState = wrap(history.replaceState);
137
-      window.addEventListener('popstate', function() {
138
-        window.ReactNativeWebView.postMessage('navigationStateChange');
139
-      });
140
-    })();
141
-
142
-    true;
143
-  `}
144
-  onMessage={({ nativeEvent: state }) => {
145
-    if (state.data === 'navigationStateChange') {
146
-      // Navigation state updated, can check state.canGoBack, etc.
147
-    }
148
-  }}
149
-/>
150
-```
151
-
152
-Thanks to [Janic Duplessis](https://github.com/react-native-community/react-native-webview/issues/24#issuecomment-483956651) for this workaround.
153
-
154 153
 ### Add support for File Upload
155 154
 
156 155
 ##### iOS
@@ -193,6 +192,12 @@ Add permission in AndroidManifest.xml:
193 192
 </manifest>
194 193
 ```
195 194
 
195
+###### Camera option availability in uploading for Android
196
+
197
+If the file input indicates that images or video is desired with [`accept`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept), then the WebView will attempt to provide options to the user to use their camera to take a picture or video.
198
+
199
+Normally, apps that do not have permission to use the camera can prompt the user to use an external app so that the requesting app has no need for permission. However, Android has made a special exception for this around the camera to reduce confusion for users. If an app _can_ request the camera permission because it has been declared, and the user has not granted the permission, it may not fire an intent that would use the camera (`MediaStore.ACTION_IMAGE_CAPTURE` or `MediaStore.ACTION_VIDEO_CAPTURE`). In this scenario, it is up to the developer to request camera permission before a file upload directly using the camera is necessary.
200
+
196 201
 ##### Check for File Upload support, with `static isFileUploadSupported()`
197 202
 
198 203
 File Upload using `<input type="file" />` is not supported for Android 4.4 KitKat (see [details](https://github.com/delight-im/Android-AdvancedWebView/issues/4#issuecomment-70372146)):
@@ -226,9 +231,23 @@ You can control **single** or **multiple** file selection by specifing the [`mul
226 231
 
227 232
 ##### iOS
228 233
 
229
-For iOS, all you need to do is specify the permissions in your `ios/[project]/Info.plist` file:
234
+On iOS, you are going to have to supply your own code to download files. You can supply an `onFileDownload` callback
235
+to the WebView component as a prop. If RNCWebView determines that a file download needs to take place, the URL where you can download the file
236
+will be given to `onFileDownload`. From that callback you can then download that file however you would like to do so.
230 237
 
231
-Save to gallery:
238
+NOTE: iOS 13+ is needed for the best possible download experience. On iOS 13 Apple added an API for accessing HTTP response headers, which
239
+is used to determine if an HTTP response should be a download. On iOS 12 or older, only MIME types that cannot be rendered by the webview will
240
+trigger calls to `onFileDownload`.
241
+
242
+Example:
243
+```javascript
244
+  onFileDownload = ({ nativeEvent }) => {
245
+    const { downloadUrl } = nativeEvent;
246
+    // --> Your download code goes here <--
247
+  }
248
+```
249
+
250
+To be able to save images to the gallery you need to specify this permission in your `ios/[project]/Info.plist` file:
232 251
 
233 252
 ```
234 253
 <key>NSPhotoLibraryAddUsageDescription</key>
@@ -237,13 +256,14 @@ Save to gallery:
237 256
 
238 257
 ##### Android
239 258
 
240
-Add permission in AndroidManifest.xml:
259
+On Android, integration with the DownloadManager is built-in.
260
+Add this permisison in AndroidManifest.xml (only required if your app supports Android versions lower than 10):
241 261
 
242 262
 ```xml
243 263
 <manifest ...>
244 264
   ......
245 265
 
246
-  <!-- this is required to save files on Android  -->
266
+  <!-- this is required to save files on Android versions lower than 10 -->
247 267
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
248 268
 
249 269
   ......
@@ -293,11 +313,50 @@ export default class App extends Component {
293 313
 
294 314
 This runs the JavaScript in the `runFirst` string once the page is loaded. In this case, you can see that both the body style was changed to red and the alert showed up after 2 seconds.
295 315
 
316
+By setting `injectedJavaScriptForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the top frame) if supported for the given platform.
317
+
296 318
 <img alt="screenshot of Github repo" width="200" src="https://user-images.githubusercontent.com/1479215/53609254-e5dc9c00-3b7a-11e9-9118-bc4e520ce6ca.png" />
297 319
 
298 320
 _Under the hood_
299 321
 
300
-> On iOS, `injectedJavaScript` runs a method on WebView called `evaluateJavaScript:completionHandler:`
322
+> On iOS, ~~`injectedJavaScript` runs a method on WebView called `evaluateJavaScript:completionHandler:`~~ – this is no longer true as of version `8.2.0`. Instead, we use a `WKUserScript` with injection time `WKUserScriptInjectionTimeAtDocumentEnd`. As a consequence, `injectedJavaScript` no longer returns an evaluation value nor logs a warning to the console. In the unlikely event that your app depended upon this behaviour, please see migration steps [here](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-574919464) to retain equivalent behaviour.
323
+> On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
324
+
325
+#### The `injectedJavaScriptBeforeContentLoaded` prop
326
+
327
+This is a script that runs **before** the web page loads for the first time. It only runs once, even if the page is reloaded or navigated away. This is useful if you want to inject anything into the window, localstorage, or document prior to the web code executing.
328
+
329
+```jsx
330
+import React, { Component } from 'react';
331
+import { View } from 'react-native';
332
+import { WebView } from 'react-native-webview';
333
+
334
+export default class App extends Component {
335
+  render() {
336
+    const runFirst = `
337
+      window.isNativeApp = true;
338
+      true; // note: this is required, or you'll sometimes get silent failures
339
+    `;
340
+    return (
341
+      <View style={{ flex: 1 }}>
342
+        <WebView
343
+          source={{
344
+            uri:
345
+              'https://github.com/react-native-community/react-native-webview',
346
+          }}
347
+          injectedJavaScriptBeforeContentLoaded={runFirst}
348
+        />
349
+      </View>
350
+    );
351
+  }
352
+}
353
+```
354
+
355
+This runs the JavaScript in the `runFirst` string before the page is loaded. In this case, the value of `window.isNativeApp` will be set to true before the web code executes.
356
+
357
+By setting `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the top frame) if supported for the given platform. Howver, although support for `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false` has been implemented for iOS and macOS, [it is not clear](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-600275750) that it is actually possible to inject JS into iframes at this point in the page lifecycle, and so relying on the expected behaviour of this prop when set to `false` is not recommended.
358
+
359
+> On iOS, ~~`injectedJavaScriptBeforeContentLoaded` runs a method on WebView called `evaluateJavaScript:completionHandler:`~~ – this is no longer true as of version `8.2.0`. Instead, we use a `WKUserScript` with injection time `WKUserScriptInjectionTimeAtDocumentStart`. As a consequence, `injectedJavaScriptBeforeContentLoaded` no longer returns an evaluation value nor logs a warning to the console. In the unlikely event that your app depended upon this behaviour, please see migration steps [here](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-574919464) to retain equivalent behaviour.
301 360
 > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
302 361
 
303 362
 #### The `injectJavaScript` method
@@ -346,7 +405,7 @@ _Under the hood_
346 405
 
347 406
 #### The `window.ReactNativeWebView.postMessage` method and `onMessage` prop
348 407
 
349
-Being able to send JavaScript to the web page is great, but what about when the web page wants to communicate back to your React Native code? This where `window.ReactNativeWebView.postMessage` and the `onMessage` prop come in.
408
+Being able to send JavaScript to the web page is great, but what about when the web page wants to communicate back to your React Native code? This is where `window.ReactNativeWebView.postMessage` and the `onMessage` prop come in.
350 409
 
351 410
 You _must_ set `onMessage` or the `window.ReactNativeWebView.postMessage` method will not be injected into the web page.
352 411
 
@@ -389,3 +448,99 @@ export default class App extends Component {
389 448
 This code will result in this alert:
390 449
 
391 450
 <img alt="Alert showing communication from web page to React Native" width="200" src="https://user-images.githubusercontent.com/1479215/53671269-7e822300-3c32-11e9-9937-7ddc34ba8af3.png" />
451
+
452
+### Working with custom headers, sessions, and cookies
453
+
454
+#### Setting Custom Headers
455
+
456
+In React Native WebView, you can set a custom header like this:
457
+
458
+```jsx
459
+<WebView
460
+  source={{
461
+    uri: 'http://example.com',
462
+    headers: {
463
+      'my-custom-header-key': 'my-custom-header-value',
464
+    },
465
+  }}
466
+/>
467
+```
468
+
469
+This will set the header on the first load, but not on subsequent page navigations.
470
+
471
+In order to work around this, you can track the current URL, intercept new page loads, and navigate to them yourself ([original credit for this technique to Chirag Shah from Big Binary](https://blog.bigbinary.com/2016/07/26/passing-request-headers-on-each-webview-request-in-react-native.html)):
472
+
473
+```jsx
474
+const CustomHeaderWebView = props => {
475
+  const { uri, onLoadStart, ...restProps } = props;
476
+  const [currentURI, setURI] = useState(props.source.uri);
477
+  const newSource = { ...props.source, uri: currentURI };
478
+
479
+  return (
480
+    <WebView
481
+      {...restProps}
482
+      source={newSource}
483
+      onShouldStartLoadWithRequest={request => {
484
+        // If we're loading the current URI, allow it to load
485
+        if (request.url === currentURI) return true;
486
+        // We're loading a new URL -- change state first
487
+        setURI(request.url);
488
+        return false;
489
+      }}
490
+    />
491
+  );
492
+};
493
+
494
+<CustomHeaderWebView
495
+  source={{
496
+    uri: 'http://example.com',
497
+    headers: {
498
+      'my-custom-header-key': 'my-custom-header-value',
499
+    },
500
+  }}
501
+/>;
502
+```
503
+
504
+#### Managing Cookies
505
+
506
+You can set cookies on the React Native side using the [@react-native-community/cookies](https://github.com/react-native-community/cookies) package.
507
+
508
+When you do, you'll likely want to enable the [sharedCookiesEnabled](Reference#sharedCookiesEnabled) prop as well.
509
+
510
+```jsx
511
+const App = () => {
512
+  return (
513
+    <WebView
514
+      source={{ uri: 'http://example.com' }}
515
+      sharedCookiesEnabled={true}
516
+    />
517
+  );
518
+};
519
+```
520
+
521
+If you'd like to send custom cookies in the WebView itself, you can do so in a custom header, like this:
522
+
523
+```jsx
524
+const App = () => {
525
+  return (
526
+    <WebView
527
+      source={{
528
+        uri: 'http://example.com',
529
+        headers: {
530
+          Cookie: 'cookie1=asdf; cookie2=dfasdfdas',
531
+        },
532
+      }}
533
+      sharedCookiesEnabled={true}
534
+    />
535
+  );
536
+};
537
+```
538
+
539
+Note that these cookies will only be sent on the first request unless you use the technique above for [setting custom headers on each page load](#Setting-Custom-Headers).
540
+
541
+### Hardware Silence Switch
542
+There are some inconsistencies in how the hardware silence switch is handled between embedded `audio` and `video` elements and between iOS and Android platforms.
543
+
544
+Audio on `iOS` will be muted when the hardware silence switch is in the on position, unless the `ignoreSilentHardwareSwitch` parameter is set to true.
545
+
546
+Video on `iOS` will always ignore the hardware silence switch.

+ 104
- 0
docs/README.portuguese.md
文件差異過大導致無法顯示
查看文件


+ 308
- 88
docs/Reference.md 查看文件

@@ -7,6 +7,9 @@ This document lays out the current public properties and methods for the React N
7 7
 - [`source`](Reference.md#source)
8 8
 - [`automaticallyAdjustContentInsets`](Reference.md#automaticallyadjustcontentinsets)
9 9
 - [`injectedJavaScript`](Reference.md#injectedjavascript)
10
+- [`injectedJavaScriptBeforeContentLoaded`](Reference.md#injectedjavascriptbeforecontentloaded)
11
+- [`injectedJavaScriptForMainFrameOnly`](Reference.md#injectedjavascriptformainframeonly)
12
+- [`injectedJavaScriptBeforeContentLoadedForMainFrameOnly`](Reference.md#injectedjavascriptbeforecontentloadedformainframeonly)
10 13
 - [`mediaPlaybackRequiresUserAction`](Reference.md#mediaplaybackrequiresuseraction)
11 14
 - [`nativeConfig`](Reference.md#nativeconfig)
12 15
 - [`onError`](Reference.md#onerror)
@@ -25,9 +28,11 @@ This document lays out the current public properties and methods for the React N
25 28
 - [`onShouldStartLoadWithRequest`](Reference.md#onshouldstartloadwithrequest)
26 29
 - [`startInLoadingState`](Reference.md#startinloadingstate)
27 30
 - [`style`](Reference.md#style)
31
+- [`containerStyle`](Reference.md#containerStyle)
28 32
 - [`decelerationRate`](Reference.md#decelerationrate)
29 33
 - [`domStorageEnabled`](Reference.md#domstorageenabled)
30 34
 - [`javaScriptEnabled`](Reference.md#javascriptenabled)
35
+- [`javaScriptCanOpenWindowsAutomatically`](Reference.md#javascriptcanopenwindowsautomatically)
31 36
 - [`androidHardwareAccelerationDisabled`](Reference.md#androidHardwareAccelerationDisabled)
32 37
 - [`mixedContentMode`](Reference.md#mixedcontentmode)
33 38
 - [`thirdPartyCookiesEnabled`](Reference.md#thirdpartycookiesenabled)
@@ -43,6 +48,7 @@ This document lays out the current public properties and methods for the React N
43 48
 - [`scrollEnabled`](Reference.md#scrollenabled)
44 49
 - [`directionalLockEnabled`](Reference.md#directionalLockEnabled)
45 50
 - [`geolocationEnabled`](Reference.md#geolocationenabled)
51
+- [`allowFileAccessFromFileURLs`](Reference.md#allowFileAccessFromFileURLs)
46 52
 - [`allowUniversalAccessFromFileURLs`](Reference.md#allowUniversalAccessFromFileURLs)
47 53
 - [`allowingReadAccessToURL`](Reference.md#allowingReadAccessToURL)
48 54
 - [`url`](Reference.md#url)
@@ -54,10 +60,13 @@ This document lays out the current public properties and methods for the React N
54 60
 - [`allowFileAccess`](Reference.md#allowFileAccess)
55 61
 - [`saveFormDataDisabled`](Reference.md#saveFormDataDisabled)
56 62
 - [`cacheEnabled`](Reference.md#cacheEnabled)
63
+- [`cacheMode`](Reference.md#cacheMode)
57 64
 - [`pagingEnabled`](Reference.md#pagingEnabled)
58 65
 - [`allowsLinkPreview`](Reference.md#allowsLinkPreview)
59 66
 - [`sharedCookiesEnabled`](Reference.md#sharedCookiesEnabled)
60 67
 - [`textZoom`](Reference.md#textZoom)
68
+- [`ignoreSilentHardwareSwitch`](Reference.md#ignoreSilentHardwareSwitch)
69
+- [`onFileDownload`](Reference.md#onFileDownload)
61 70
 - [`autoManageStatusBarEnabled`](Reference.md#autoManageStatusBarEnabled)
62 71
 
63 72
 ## Methods Index
@@ -68,6 +77,11 @@ This document lays out the current public properties and methods for the React N
68 77
 - [`reload`](Reference.md#reload)
69 78
 - [`stopLoading`](Reference.md#stoploading)
70 79
 - [`injectJavaScript`](Reference.md#injectjavascriptstr)
80
+- [`clearFormData`](Reference.md#clearFormData)
81
+- [`clearCache`](Reference.md#clearCache)
82
+- [`clearHistory`](Reference.md#clearHistory)
83
+- [`requestFocus`](Reference.md#requestFocus)
84
+- [`postMessage`](Reference.md#postMessage)
71 85
 
72 86
 ---
73 87
 
@@ -83,9 +97,9 @@ The object passed to `source` can have either of the following shapes:
83 97
 
84 98
 **Load uri**
85 99
 
86
-- `uri` (string) - The URI to load in the `WebView`. Can be a local or remote file.
100
+- `uri` (string) - The URI to load in the `WebView`. Can be a local or remote file, and can be changed with React state or props to navigate to a new page.
87 101
 - `method` (string) - The HTTP Method to use. Defaults to GET if not specified. On Android, the only supported methods are GET and POST.
88
-- `headers` (object) - Additional HTTP headers to send with the request. On Android, this can only be used with GET requests.
102
+- `headers` (object) - Additional HTTP headers to send with the request. On Android, this can only be used with GET requests. See the [Guide](Guide.md#setting-custom-headers) for more information on setting custom headers.
89 103
 - `body` (string) - The HTTP body to send with the request. This must be a valid UTF-8 string, and will be sent exactly as specified, with no additional encoding (e.g. URL-escaping or base64) applied. On Android, this can only be used with POST requests.
90 104
 
91 105
 **Static HTML**
@@ -113,11 +127,15 @@ Controls whether to adjust the content inset for web views that are placed behin
113 127
 
114 128
 ### `injectedJavaScript`
115 129
 
116
-Set this to provide JavaScript that will be injected into the web page when the view loads. Make sure the string evaluates to a valid type (`true` works) and doesn't otherwise throw an exception.
130
+Set this to provide JavaScript that will be injected into the web page after the document finishes loading, but before other subresources finish loading.
117 131
 
118
-| Type   | Required |
119
-| ------ | -------- |
120
-| string | No       |
132
+Make sure the string evaluates to a valid type (`true` works) and doesn't otherwise throw an exception.
133
+
134
+On iOS, see [`WKUserScriptInjectionTimeAtDocumentEnd`](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime/wkuserscriptinjectiontimeatdocumentend?language=objc)
135
+
136
+| Type   | Required | Platform |
137
+| ------ | -------- | -------- |
138
+| string | No       | iOS, Android, macOS
121 139
 
122 140
 To learn more, read the [Communicating between JS and Native](Guide.md#communicating-between-js-and-native) guide.
123 141
 
@@ -131,7 +149,7 @@ const INJECTED_JAVASCRIPT = `(function() {
131 149
 })();`;
132 150
 
133 151
 <WebView
134
-  source={{ uri: 'https://facebook.github.io/react-native' }}
152
+  source={{ uri: 'https://reactnative.dev' }}
135 153
   injectedJavaScript={INJECTED_JAVASCRIPT}
136 154
   onMessage={this.onMessage}
137 155
 />;
@@ -139,15 +157,73 @@ const INJECTED_JAVASCRIPT = `(function() {
139 157
 
140 158
 ---
141 159
 
160
+### `injectedJavaScriptBeforeContentLoaded`
161
+
162
+Set this to provide JavaScript that will be injected into the web page after the document element is created, but before other subresources finish loading.
163
+
164
+Make sure the string evaluates to a valid type (`true` works) and doesn't otherwise throw an exception.
165
+
166
+On iOS, see [`WKUserScriptInjectionTimeAtDocumentStart`](https://developer.apple.com/documentation/webkit/wkuserscriptinjectiontime/wkuserscriptinjectiontimeatdocumentstart?language=objc)
167
+
168
+| Type   | Required | Platform |
169
+| ------ | -------- | -------- |
170
+| string | No       | iOS, macOS |
171
+
172
+To learn more, read the [Communicating between JS and Native](Guide.md#communicating-between-js-and-native) guide.
173
+
174
+Example:
175
+
176
+Post message a JSON object of `window.location` to be handled by [`onMessage`](Reference.md#onmessage). `window.ReactNativeWebView.postMessage` *will* be available at this time.
177
+
178
+```jsx
179
+const INJECTED_JAVASCRIPT = `(function() {
180
+    window.ReactNativeWebView.postMessage(JSON.stringify(window.location));
181
+})();`;
182
+
183
+<WebView
184
+  source={{ uri: 'https://reactnative.dev' }}
185
+  injectedJavaScriptBeforeContentLoaded={INJECTED_JAVASCRIPT}
186
+  onMessage={this.onMessage}
187
+/>;
188
+```
189
+
190
+---
191
+
192
+### `injectedJavaScriptForMainFrameOnly`
193
+
194
+If `true` (default), loads the `injectedJavaScript` only into the main frame.
195
+
196
+If `false`, loads it into all frames (e.g. iframes).
197
+
198
+| Type   | Required | Platform |
199
+| ------ | -------- | -------- |
200
+| bool | No       | iOS, macOS       |
201
+
202
+---
203
+
204
+### `injectedJavaScriptBeforeContentLoadedForMainFrameOnly`
205
+
206
+If `true` (default), loads the `injectedJavaScriptBeforeContentLoaded` only into the main frame.
207
+
208
+If `false`, loads it into all frames (e.g. iframes).
209
+
210
+Warning: although support for `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false` has been implemented for iOS and macOS, [it is not clear](https://github.com/react-native-community/react-native-webview/pull/1119#issuecomment-600275750) that it is actually possible to inject JS into iframes at this point in the page lifecycle, and so relying on the expected behaviour of this prop when set to `false` is not recommended.
211
+
212
+| Type   | Required | Platform |
213
+| ------ | -------- | -------- |
214
+| bool | No       | iOS, macOS       |
215
+
216
+---
217
+
142 218
 ### `mediaPlaybackRequiresUserAction`
143 219
 
144 220
 Boolean that determines whether HTML5 audio and video requires the user to tap them before they start playing. The default value is `true`. (Android API minimum version 17).
145 221
 
146 222
 NOTE: the default `true` value might cause some videos to hang loading on iOS. Setting it to `false` could fix this issue.
147 223
 
148
-| Type | Required |
149
-| ---- | -------- |
150
-| bool | No       |
224
+| Type | Required | Platform |
225
+| ---- | -------- | -------- |
226
+| bool | No       | iOS, Android, macOS |
151 227
 
152 228
 ---
153 229
 
@@ -161,9 +237,9 @@ The `nativeConfig` prop expects an object with the following keys:
161 237
 - `props` (object)
162 238
 - `viewManager` (object)
163 239
 
164
-| Type   | Required |
165
-| ------ | -------- |
166
-| object | No       |
240
+| Type   | Required | Platform |
241
+| ------ | -------- | -------- |
242
+| object | No       | iOS, Android, macOS |
167 243
 
168 244
 ---
169 245
 
@@ -179,7 +255,7 @@ Example:
179 255
 
180 256
 ```jsx
181 257
 <WebView
182
-  source={{ uri: 'https://facebook.github.io/react-native' }}
258
+  source={{ uri: 'https://reactnative.dev' }}
183 259
   onError={syntheticEvent => {
184 260
     const { nativeEvent } = syntheticEvent;
185 261
     console.warn('WebView error: ', nativeEvent);
@@ -219,7 +295,7 @@ Example:
219 295
 
220 296
 ```jsx
221 297
 <WebView
222
-  source={{ uri: 'https://facebook.github.io/react-native' }}
298
+  source={{ uri: 'https://reactnative.dev' }}
223 299
   onLoad={syntheticEvent => {
224 300
     const { nativeEvent } = syntheticEvent;
225 301
     this.url = nativeEvent.url;
@@ -252,7 +328,7 @@ Example:
252 328
 
253 329
 ```jsx
254 330
 <WebView
255
-  source={{ uri: 'https://facebook.github.io/react-native' }}
331
+  source={{ uri: 'https://reactnative.dev' }}
256 332
   onLoadEnd={syntheticEvent => {
257 333
     // update component to be aware of loading status
258 334
     const { nativeEvent } = syntheticEvent;
@@ -286,7 +362,7 @@ Example:
286 362
 
287 363
 ```jsx
288 364
 <WebView
289
-  source={{ uri: 'https://facebook.github.io/react-native/=' }}
365
+  source={{ uri: 'https://reactnative.dev/=' }}
290 366
   onLoadStart={syntheticEvent => {
291 367
     // update component to be aware of loading status
292 368
     const { nativeEvent } = syntheticEvent;
@@ -312,15 +388,15 @@ url
312 388
 
313 389
 Function that is invoked when the `WebView` is loading.
314 390
 
315
-| Type     | Required |
316
-| -------- | -------- |
317
-| function | No       |
391
+| Type     | Required | Platform |
392
+| -------- | -------- | --------- |
393
+| function | No       | iOS, Android, macOS |
318 394
 
319 395
 Example:
320 396
 
321 397
 ```jsx
322 398
 <WebView
323
-  source={{ uri: 'https://facebook.github.io/react-native' }}
399
+  source={{ uri: 'https://reactnative.dev' }}
324 400
   onLoadProgress={({ nativeEvent }) => {
325 401
     this.loadingProgress = nativeEvent.progress;
326 402
   }}
@@ -356,7 +432,7 @@ Example:
356 432
 
357 433
 ```jsx
358 434
 <WebView
359
-  source={{ uri: 'https://facebook.github.io/react-native' }}
435
+  source={{ uri: 'https://reactnative.dev' }}
360 436
   onHttpError={syntheticEvent => {
361 437
     const { nativeEvent } = syntheticEvent;
362 438
     console.warn(
@@ -411,7 +487,7 @@ Example:
411 487
 
412 488
 ```jsx
413 489
 <WebView
414
-  source={{ uri: 'https://facebook.github.io/react-native' }}
490
+  source={{ uri: 'https://reactnative.dev' }}
415 491
   onNavigationStateChange={navState => {
416 492
     // Keep track of going back navigation within component
417 493
     this.canGoBack = navState.canGoBack;
@@ -439,15 +515,15 @@ Note that this method will not be invoked on hash URL changes (e.g. from `https:
439 515
 
440 516
 Function that is invoked when the `WebView` content process is terminated.
441 517
 
442
-| Type     | Required | Platform      |
443
-| -------- | -------- | ------------- |
444
-| function | No       | iOS WKWebView |
518
+| Type     | Required | Platform                |
519
+| -------- | -------- | ----------------------- |
520
+| function | No       | iOS and macOS WKWebView |
445 521
 
446 522
 Example:
447 523
 
448 524
 ```jsx
449 525
 <WebView
450
-  source={{ uri: 'https://facebook.github.io/react-native' }}
526
+  source={{ uri: 'https://reactnative.dev' }}
451 527
   onContentProcessDidTerminate={syntheticEvent => {
452 528
     const { nativeEvent } = syntheticEvent;
453 529
     console.warn('Content process terminated, reloading', nativeEvent);
@@ -473,16 +549,16 @@ url
473 549
 
474 550
 List of origin strings to allow being navigated to. The strings allow wildcards and get matched against _just_ the origin (not the full URL). If the user taps to navigate to a new page but the new page is not in this whitelist, the URL will be handled by the OS. The default whitelisted origins are "http://*" and "https://*".
475 551
 
476
-| Type             | Required |
477
-| ---------------- | -------- |
478
-| array of strings | No       |
552
+| Type             | Required | Platform |
553
+| ---------------- | -------- | -------- |
554
+| array of strings | No       | iOS, Android, macOS |
479 555
 
480 556
 Example:
481 557
 
482 558
 ```jsx
483 559
 //only allow URIs that begin with https:// or git://
484 560
 <WebView
485
-  source={{ uri: 'https://facebook.github.io/react-native' }}
561
+  source={{ uri: 'https://reactnative.dev' }}
486 562
   originWhitelist={['https://*', 'git://*']}
487 563
 />
488 564
 ```
@@ -493,15 +569,15 @@ Example:
493 569
 
494 570
 Function that returns a view to show if there's an error.
495 571
 
496
-| Type     | Required |
497
-| -------- | -------- |
498
-| function | No       |
572
+| Type     | Required | Platform |
573
+| -------- | -------- | -------- |
574
+| function | No       | iOS, Android, macOS |
499 575
 
500 576
 Example:
501 577
 
502 578
 ```jsx
503 579
 <WebView
504
-  source={{ uri: 'https://facebook.github.io/react-native' }}
580
+  source={{ uri: 'https://reactnative.dev' }}
505 581
   renderError={errorName => <Error name={errorName} />}
506 582
 />
507 583
 ```
@@ -514,15 +590,15 @@ The function passed to `renderError` will be called with the name of the error
514 590
 
515 591
 Function that returns a loading indicator. The startInLoadingState prop must be set to true in order to use this prop.
516 592
 
517
-| Type     | Required |
518
-| -------- | -------- |
519
-| function | No       |
593
+| Type     | Required | Platform |
594
+| -------- | -------- | -------- |
595
+| function | No       | iOS, Android, macOS |
520 596
 
521 597
 Example:
522 598
 
523 599
 ```jsx
524 600
 <WebView
525
-  source={{ uri: 'https://facebook.github.io/react-native' }}
601
+  source={{ uri: 'https://reactnative.dev' }}
526 602
   startInLoadingState={true}
527 603
   renderLoading={() => <Loading />}
528 604
 />
@@ -546,18 +622,18 @@ Function that allows custom handling of any web view requests. Return `true` fro
546 622
 
547 623
 On Android, is not called on the first load.
548 624
 
549
-| Type     | Required |
550
-| -------- | -------- |
551
-| function | No       |
625
+| Type     | Required | Platform |
626
+| -------- | -------- | -------- |
627
+| function | No       | iOS, Android, macOS |
552 628
 
553 629
 Example:
554 630
 
555 631
 ```jsx
556 632
 <WebView
557
-  source={{ uri: 'https://facebook.github.io/react-native' }}
633
+  source={{ uri: 'https://reactnative.dev' }}
558 634
   onShouldStartLoadWithRequest={request => {
559 635
     // Only allow navigating within this website
560
-    return request.url.startsWith('https://facebook.github.io/react-native');
636
+    return request.url.startsWith('https://reactnative.dev');
561 637
   }}
562 638
 />
563 639
 ```
@@ -582,9 +658,9 @@ navigationType
582 658
 
583 659
 Boolean value that forces the `WebView` to show the loading view on the first load. This prop must be set to `true` in order for the `renderLoading` prop to work.
584 660
 
585
-| Type | Required |
586
-| ---- | -------- |
587
-| bool | No       |
661
+| Type | Required | Platform |
662
+| ---- | -------- | -------- |
663
+| bool | No       | iOS, Android, macOS |
588 664
 
589 665
 ---
590 666
 
@@ -600,13 +676,32 @@ Example:
600 676
 
601 677
 ```jsx
602 678
 <WebView
603
-  source={{ uri: 'https://facebook.github.io/react-native' }}
679
+  source={{ uri: 'https://reactnative.dev' }}
604 680
   style={{ marginTop: 20 }}
605 681
 />
606 682
 ```
607 683
 
608 684
 ---
609 685
 
686
+### `containerStyle`
687
+
688
+A style object that allow you to customize the `WebView` container style. Please note that there are default styles (example: you need to add `flex: 0` to the style if you want to use `height` property).
689
+
690
+| Type  | Required |
691
+| ----- | -------- |
692
+| style | No       |
693
+
694
+Example:
695
+
696
+```jsx
697
+<WebView
698
+  source={{ uri: 'https://reactnative.dev' }}
699
+  containerStyle={{ marginTop: 20 }}
700
+/>
701
+```
702
+
703
+---
704
+
610 705
 ### `decelerationRate`
611 706
 
612 707
 A floating-point number that determines how quickly the scroll view decelerates after the user lifts their finger. You may also use the string shortcuts `"normal"` and `"fast"` which match the underlying iOS settings for `UIScrollViewDecelerationRateNormal` and `UIScrollViewDecelerationRateFast` respectively:
@@ -632,11 +727,21 @@ Boolean value to control whether DOM Storage is enabled. Used only in Android.
632 727
 
633 728
 ### `javaScriptEnabled`
634 729
 
635
-Boolean value to enable JavaScript in the `WebView`. Used on Android only as JavaScript is enabled by default on iOS. The default value is `true`.
730
+Boolean value to enable JavaScript in the `WebView`. The default value is `true`.
636 731
 
637
-| Type | Required | Platform |
638
-| ---- | -------- | -------- |
639
-| bool | No       | Android  |
732
+| Type | Required |
733
+| ---- | -------- |
734
+| bool | No       |
735
+
736
+---
737
+
738
+### `javaScriptCanOpenWindowsAutomatically`
739
+
740
+A Boolean value indicating whether JavaScript can open windows without user interaction. The default value is `false`.
741
+
742
+| Type | Required |
743
+| ---- | -------- |
744
+| bool | No       |
640 745
 
641 746
 ---
642 747
 
@@ -668,7 +773,7 @@ Possible values for `mixedContentMode` are:
668 773
 
669 774
 ### `thirdPartyCookiesEnabled`
670 775
 
671
-Boolean value to enable third party cookies in the `WebView`. Used on Android Lollipop and above only as third party cookies are enabled by default on Android Kitkat and below and on iOS. The default value is `true`.
776
+Boolean value to enable third party cookies in the `WebView`. Used on Android Lollipop and above only as third party cookies are enabled by default on Android Kitkat and below and on iOS. The default value is `true`. For more on cookies, read the [Guide](Guide.md#Managing-Cookies)
672 777
 
673 778
 | Type | Required | Platform |
674 779
 | ---- | -------- | -------- |
@@ -680,9 +785,9 @@ Boolean value to enable third party cookies in the `WebView`. Used on Android Lo
680 785
 
681 786
 Sets the user-agent for the `WebView`.
682 787
 
683
-| Type   | Required |
684
-| ------ | -------- |
685
-| string | No       |
788
+| Type   | Required | Platform |
789
+| ------ | -------- | -------- |
790
+| string | No       | iOS, Android, macOS |
686 791
 
687 792
 ---
688 793
 
@@ -690,13 +795,13 @@ Sets the user-agent for the `WebView`.
690 795
 
691 796
 Append to the existing user-agent. Setting `userAgent` will override this.
692 797
 
693
-| Type   | Required |
694
-| ------ | -------- |
695
-| string | No       |
798
+| Type   | Required | Platform |
799
+| ------ | -------- | -------- |
800
+| string | No       | iOS, Android, macOS |
696 801
 
697 802
 ```jsx
698 803
 <WebView
699
-  source={{ uri: 'https://facebook.github.io/react-native' }}
804
+  source={{ uri: 'https://reactnative.dev' }}
700 805
   applicationNameForUserAgent={'DemoApp/1.1.0'}
701 806
 />
702 807
 // Resulting User-Agent will look like:
@@ -809,9 +914,9 @@ Possible values for `dataDetectorTypes` are:
809 914
 
810 915
 Boolean value that determines whether scrolling is enabled in the `WebView`. The default value is `true`. Setting this to `false` will prevent the webview from moving the document body when the keyboard appears over an input.
811 916
 
812
-| Type | Required | Platform |
813
-| ---- | -------- | -------- |
814
-| bool | No       | iOS      |
917
+| Type | Required | Platform      |
918
+| ---- | -------- | ------------- |
919
+| bool | No       | iOS and macOS |
815 920
 
816 921
 ---
817 922
 
@@ -830,9 +935,9 @@ The default value is `true`.
830 935
 
831 936
 Boolean value that determines whether a horizontal scroll indicator is shown in the `WebView`. The default value is `true`.
832 937
 
833
-| Type | Required |
834
-| ---- | -------- |
835
-| bool | No       |
938
+| Type | Required | Platform |
939
+| ---- | -------- | -------- |
940
+| bool | No       | iOS, Android, macOS |
836 941
 
837 942
 ---
838 943
 
@@ -840,9 +945,9 @@ Boolean value that determines whether a horizontal scroll indicator is shown in
840 945
 
841 946
 Boolean value that determines whether a vertical scroll indicator is shown in the `WebView`. The default value is `true`.
842 947
 
843
-| Type | Required |
844
-| ---- | -------- |
845
-| bool | No       |
948
+| Type | Required | Platform |
949
+| ---- | -------- | -------- |
950
+| bool | No       | iOS, Android, macOS |
846 951
 
847 952
 ---
848 953
 
@@ -856,6 +961,16 @@ Set whether Geolocation is enabled in the `WebView`. The default value is `false
856 961
 
857 962
 ---
858 963
 
964
+### `allowFileAccessFromFileURLs`
965
+
966
+Boolean that sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from other file scheme URLs. The default value is `false`.
967
+
968
+| Type | Required | Platform |
969
+| ---- | -------- | -------- |
970
+| bool | No       | iOS, Android, macOS |
971
+
972
+---
973
+
859 974
 ### `allowUniversalAccessFromFileURLs`
860 975
 
861 976
 Boolean that sets whether JavaScript running in the context of a file scheme URL should be allowed to access content from any origin. Including accessing content from other file scheme URLs. The default value is `false`.
@@ -870,9 +985,9 @@ Boolean that sets whether JavaScript running in the context of a file scheme URL
870 985
 
871 986
 A String value that indicates which URLs the WebView's file can then reference in scripts, AJAX requests, and CSS imports. This is only used in for WebViews that are loaded with a source.uri set to a `'file://'` URL. If not provided, the default is to only allow read access to the URL provided in source.uri itself.
872 987
 
873
-| Type   | Required | Platform |
874
-| ------ | -------- | -------- |
875
-| string | No       | iOS      |
988
+| Type   | Required | Platform      |
989
+| ------ | -------- | ------------- |
990
+| string | No       | iOS and macOS |
876 991
 
877 992
 ---
878 993
 
@@ -920,9 +1035,9 @@ If true, this will hide the keyboard accessory view (< > and Done).
920 1035
 
921 1036
 If true, this will be able horizontal swipe gestures. The default value is `false`.
922 1037
 
923
-| Type    | Required | Platform |
924
-| ------- | -------- | -------- |
925
-| boolean | No       | iOS      |
1038
+| Type    | Required | Platform      |
1039
+| ------- | -------- | ------------- |
1040
+| boolean | No       | iOS and macOS |
926 1041
 
927 1042
 ---
928 1043
 
@@ -930,9 +1045,9 @@ If true, this will be able horizontal swipe gestures. The default value is `fals
930 1045
 
931 1046
 Does not store any data within the lifetime of the WebView.
932 1047
 
933
-| Type    | Required |
934
-| ------- | -------- |
935
-| boolean | No       |
1048
+| Type    | Required | Platform |
1049
+| ------- | -------- | -------- |
1050
+| boolean | No       | iOS, Android, macOS |
936 1051
 
937 1052
 ---
938 1053
 
@@ -960,9 +1075,26 @@ Sets whether the WebView should disable saving form data. The default value is `
960 1075
 
961 1076
 Sets whether WebView should use browser caching.
962 1077
 
963
-| Type    | Required | Default |
964
-| ------- | -------- | ------- |
965
-| boolean | No       | true    |
1078
+| Type    | Required | Default | Platform |
1079
+| ------- | -------- | ------- | -------- |
1080
+| boolean | No       | true    | iOS, Android, macOS |
1081
+
1082
+---
1083
+
1084
+### `cacheMode`
1085
+
1086
+Overrides the way the cache is used. The way the cache is used is based on the navigation type. For a normal page load, the cache is checked and content is re-validated as needed. When navigating back, content is not revalidated, instead the content is just retrieved from the cache. This property allows the client to override this behavior.
1087
+
1088
+Possible values are:
1089
+
1090
+- `LOAD_DEFAULT` - Default cache usage mode. If the navigation type doesn't impose any specific behavior, use cached resources when they are available and not expired, otherwise load resources from the network.
1091
+- `LOAD_CACHE_ELSE_NETWORK` - Use cached resources when they are available, even if they have expired. Otherwise load resources from the network.
1092
+- `LOAD_NO_CACHE` - Don't use the cache, load from the network.
1093
+- `LOAD_CACHE_ONLY` - Don't use the network, load from the cache.
1094
+
1095
+| Type   | Required | Default      | Platform |
1096
+| ------ | -------- | ------------ | -------- |
1097
+| string | No       | LOAD_DEFAULT | Android  |
966 1098
 
967 1099
 ---
968 1100
 
@@ -980,19 +1112,19 @@ If the value of this property is true, the scroll view stops on multiples of the
980 1112
 
981 1113
 A Boolean value that determines whether pressing on a link displays a preview of the destination for the link. In iOS this property is available on devices that support 3D Touch. In iOS 10 and later, the default value is true; before that, the default value is false.
982 1114
 
983
-| Type    | Required | Platform |
984
-| ------- | -------- | -------- |
985
-| boolean | No       | iOS      |
1115
+| Type    | Required | Platform      |
1116
+| ------- | -------- | ------------- |
1117
+| boolean | No       | iOS and macOS |
986 1118
 
987 1119
 ---
988 1120
 
989 1121
 ### `sharedCookiesEnabled`
990 1122
 
991
-Set `true` if shared cookies from `[NSHTTPCookieStorage sharedHTTPCookieStorage]` should used for every load request in the WebView. The default value is `false`.
1123
+Set `true` if shared cookies from `[NSHTTPCookieStorage sharedHTTPCookieStorage]` should used for every load request in the WebView. The default value is `false`. For more on cookies, read the [Guide](Guide.md#Managing-Cookies)
992 1124
 
993
-| Type    | Required | Platform |
994
-| ------- | -------- | -------- |
995
-| boolean | No       | iOS      |
1125
+| Type    | Required | Platform      |
1126
+| ------- | -------- | ------------- |
1127
+| boolean | No       | iOS and macOS |
996 1128
 
997 1129
 ---
998 1130
 
@@ -1010,6 +1142,49 @@ Example:
1010 1142
 
1011 1143
 `<WebView textZoom={100} />`
1012 1144
 
1145
+### `ignoreSilentHardwareSwitch`
1146
+
1147
+(ios only)
1148
+
1149
+When set to true the hardware silent switch is ignored. Default: `false`
1150
+
1151
+| Type    | Required | Platform |
1152
+| ------- | -------- | -------- |
1153
+| boolean | No       | iOS      |
1154
+
1155
+### `onFileDownload`
1156
+This property is iOS-only.
1157
+
1158
+Function that is invoked when the client needs to download a file.
1159
+
1160
+iOS 13+ only: If the webview navigates to a URL that results in an HTTP
1161
+response with a Content-Disposition header 'attachment...', then
1162
+this will be called.
1163
+
1164
+iOS 8+: If the MIME type indicates that the content is not renderable by the
1165
+webview, that will also cause this to be called. On iOS versions before 13,
1166
+this is the only condition that will cause this function to be called.
1167
+
1168
+The application will need to provide its own code to actually download
1169
+the file.
1170
+
1171
+If not provided, the default is to let the webview try to render the file.
1172
+
1173
+Example:
1174
+```jsx
1175
+<WebView
1176
+  source={{ uri: 'https://reactnative.dev' }}
1177
+  onFileDownload={ ( { nativeEvent: { downloadUrl } } ) => {
1178
+    // You use downloadUrl which is a string to download files however you want.
1179
+  }}
1180
+  />
1181
+```
1182
+
1183
+| Type    | Required | Platform |
1184
+| ------- | -------- | -------- |
1185
+| function | No       | iOS      |
1186
+
1187
+
1013 1188
 ---
1014 1189
 
1015 1190
 ### `autoManageStatusBarEnabled`
@@ -1074,6 +1249,51 @@ Executes the JavaScript string.
1074 1249
 
1075 1250
 To learn more, read the [Communicating between JS and Native](Guide.md#communicating-between-js-and-native) guide.
1076 1251
 
1252
+### `requestFocus()`
1253
+
1254
+```javascript
1255
+requestFocus();
1256
+```
1257
+
1258
+Request the webView to ask for focus. (People working on TV apps might want having a look at this!)
1259
+
1260
+### `postMessage(str)`
1261
+
1262
+```javascript
1263
+postMessage('message');
1264
+```
1265
+Post a message to WebView, handled by [`onMessage`](Reference.md#onmessage).
1266
+
1267
+### `clearFormData()`
1268
+
1269
+(android only)
1270
+
1271
+```javascript
1272
+clearFormData();
1273
+```
1274
+
1275
+Removes the autocomplete popup from the currently focused form field, if present. [developer.android.com reference](<https://developer.android.com/reference/android/webkit/WebView.html#clearFormData()>)
1276
+
1277
+### `clearCache(bool)`
1278
+
1279
+(android only)
1280
+
1281
+```javascript
1282
+clearCache(true);
1283
+```
1284
+
1285
+Clears the resource cache. Note that the cache is per-application, so this will clear the cache for all WebViews used. [developer.android.com reference](<https://developer.android.com/reference/android/webkit/WebView.html#clearCache(boolean)>)
1286
+
1287
+### `clearHistory()`
1288
+
1289
+(android only)
1290
+
1291
+```javascript
1292
+clearHistory();
1293
+```
1294
+
1295
+Tells this WebView to clear its internal back/forward list. [developer.android.com reference](<https://developer.android.com/reference/android/webkit/WebView.html#clearHistory()>)
1296
+
1077 1297
 ## Other Docs
1078 1298
 
1079 1299
 Also check out our [Getting Started Guide](Getting-Started.md) and [In-Depth Guide](Guide.md).

+ 1
- 0
example/.gitattributes 查看文件

@@ -0,0 +1 @@
1
+*.pbxproj -text

+ 67
- 0
example/.gitignore 查看文件

@@ -0,0 +1,67 @@
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
+# exclude project.xcworkspace except for xcshareddata/WorkspaceSettings.xcsettings
24
+project.xcworkspace/*
25
+**/project.xcworkspace/contents.xcworkspacedata
26
+**/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
27
+
28
+# Android/IntelliJ
29
+#
30
+build/
31
+.idea
32
+.gradle
33
+local.properties
34
+*.iml
35
+
36
+# Visual Studio Code
37
+#
38
+.vscode/
39
+
40
+# node.js
41
+#
42
+node_modules/
43
+npm-debug.log
44
+yarn-error.log
45
+
46
+# BUCK
47
+buck-out/
48
+\.buckd/
49
+*.keystore
50
+!debug.keystore
51
+
52
+# fastlane
53
+#
54
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
55
+# screenshots whenever they are needed.
56
+# For more information about the recommended setup visit:
57
+# https://docs.fastlane.tools/best-practices/source-control/
58
+
59
+*/fastlane/report.xml
60
+*/fastlane/Preview.html
61
+*/fastlane/screenshots
62
+
63
+# Bundle artifact
64
+*.jsbundle
65
+
66
+# CocoaPods
67
+/ios/Pods/

+ 6
- 0
example/.prettierrc.js 查看文件

@@ -0,0 +1,6 @@
1
+module.exports = {
2
+  bracketSpacing: false,
3
+  jsxBracketSameLine: true,
4
+  singleQuote: true,
5
+  trailingComma: 'all',
6
+};

+ 1
- 0
example/.watchmanconfig 查看文件

@@ -0,0 +1 @@
1
+{}

+ 216
- 0
example/App.tsx 查看文件

@@ -0,0 +1,216 @@
1
+import React, {Component} from 'react';
2
+import {
3
+  StyleSheet,
4
+  SafeAreaView,
5
+  Text,
6
+  TouchableOpacity,
7
+  View,
8
+  Keyboard,
9
+  Button,
10
+  Platform,
11
+} from 'react-native';
12
+
13
+import Alerts from './examples/Alerts';
14
+import Scrolling from './examples/Scrolling';
15
+import Background from './examples/Background';
16
+import Downloads from './examples/Downloads';
17
+import Uploads from './examples/Uploads';
18
+import Injection from './examples/Injection';
19
+import LocalPageLoad from './examples/LocalPageLoad';
20
+
21
+const TESTS = {
22
+  Alerts: {
23
+    title: 'Alerts',
24
+    testId: 'alerts',
25
+    description: 'Alerts tests',
26
+    render() {
27
+      return <Alerts />;
28
+    },
29
+  },
30
+  Scrolling: {
31
+    title: 'Scrolling',
32
+    testId: 'scrolling',
33
+    description: 'Scrolling event test',
34
+    render() {
35
+      return <Scrolling />;
36
+    },
37
+  },
38
+  Background: {
39
+    title: 'Background',
40
+    testId: 'background',
41
+    description: 'Background color test',
42
+    render() {
43
+      return <Background />;
44
+    },
45
+  },
46
+  Downloads: {
47
+    title: 'Downloads',
48
+    testId: 'downloads',
49
+    description: 'File downloads test',
50
+    render() {
51
+      return <Downloads />;
52
+    },
53
+  },
54
+  Uploads: {
55
+    title: 'Uploads',
56
+    testId: 'uploads',
57
+    description: 'Upload test',
58
+    render() {
59
+      return <Uploads />;
60
+    },
61
+  },
62
+  Injection: {
63
+    title: 'Injection',
64
+    testId: 'injection',
65
+    description: 'Injection test',
66
+    render() {
67
+      return <Injection />;
68
+    },
69
+  },
70
+  PageLoad: {
71
+    title: 'LocalPageLoad',
72
+    testId: 'LocalPageLoad',
73
+    description: 'Local Page load test',
74
+    render() {
75
+      return <LocalPageLoad />;
76
+    },
77
+  },
78
+};
79
+
80
+type Props = {};
81
+type State = {restarting: boolean, currentTest: Object};
82
+
83
+export default class App extends Component<Props, State> {
84
+  state = {
85
+    restarting: false,
86
+    currentTest: TESTS.Alerts,
87
+  };
88
+
89
+  _simulateRestart = () => {
90
+    this.setState({restarting: true}, () => this.setState({restarting: false}));
91
+  };
92
+
93
+  _changeTest = testName => {
94
+    this.setState({currentTest: TESTS[testName]});
95
+  };
96
+
97
+  render() {
98
+    const {restarting, currentTest} = this.state;
99
+    return (
100
+      <SafeAreaView style={styles.container}>
101
+        <TouchableOpacity
102
+          style={styles.closeKeyboardView}
103
+          onPress={() => Keyboard.dismiss()}
104
+          testID="closeKeyboard"
105
+        />
106
+
107
+        <TouchableOpacity
108
+          testID="restart_button"
109
+          onPress={this._simulateRestart}
110
+          style={styles.restartButton}
111
+          activeOpacity={0.6}>
112
+          <Text>Simulate Restart</Text>
113
+        </TouchableOpacity>
114
+
115
+        <View style={styles.testPickerContainer}>
116
+          <Button
117
+            testID="testType_alerts"
118
+            title="Alerts"
119
+            onPress={() => this._changeTest('Alerts')}
120
+          />
121
+          <Button
122
+            testID="testType_scrolling"
123
+            title="Scrolling"
124
+            onPress={() => this._changeTest('Scrolling')}
125
+          />
126
+          <Button
127
+            testID="testType_background"
128
+            title="Background"
129
+            onPress={() => this._changeTest('Background')}
130
+          />
131
+          <Button
132
+            testID="testType_injection"
133
+            title="Injection"
134
+            onPress={() => this._changeTest('Injection')}
135
+          />
136
+          <Button
137
+            testID="testType_pageLoad"
138
+            title="LocalPageLoad"
139
+            onPress={() => this._changeTest('PageLoad')}
140
+          />
141
+          {Platform.OS == "ios" && <Button
142
+            testID="testType_downloads"
143
+            title="Downloads"
144
+            onPress={() => this._changeTest('Downloads')}
145
+          />}
146
+          {Platform.OS === 'android' && <Button
147
+            testID="testType_uploads"
148
+            title="Uploads"
149
+            onPress={() => this._changeTest('Uploads')}
150
+          />}
151
+        </View>
152
+
153
+        {restarting ? null : (
154
+          <View
155
+            testID={`example-${currentTest.testId}`}
156
+            key={currentTest.title}
157
+            style={styles.exampleContainer}>
158
+            <Text style={styles.exampleTitle}>{currentTest.title}</Text>
159
+            <Text style={styles.exampleDescription}>
160
+              {currentTest.description}
161
+            </Text>
162
+            <View style={styles.exampleInnerContainer}>
163
+              {currentTest.render()}
164
+            </View>
165
+          </View>
166
+        )}
167
+      </SafeAreaView>
168
+    );
169
+  }
170
+}
171
+
172
+const styles = StyleSheet.create({
173
+  container: {
174
+    flex: 1,
175
+    backgroundColor: '#F5FCFF',
176
+    padding: 8,
177
+  },
178
+  exampleContainer: {
179
+    padding: 16,
180
+    backgroundColor: '#FFF',
181
+    borderColor: '#EEE',
182
+    borderTopWidth: 1,
183
+    borderBottomWidth: 1,
184
+    flex: 1,
185
+  },
186
+  exampleTitle: {
187
+    fontSize: 18,
188
+  },
189
+  exampleDescription: {
190
+    color: '#333333',
191
+    marginBottom: 16,
192
+  },
193
+  exampleInnerContainer: {
194
+    borderColor: '#EEE',
195
+    borderTopWidth: 1,
196
+    paddingTop: 10,
197
+    flex: 1,
198
+  },
199
+  restartButton: {
200
+    padding: 6,
201
+    fontSize: 16,
202
+    borderRadius: 5,
203
+    backgroundColor: '#F3F3F3',
204
+    alignItems: 'center',
205
+    justifyContent: 'center',
206
+    alignSelf: 'flex-end',
207
+  },
208
+  closeKeyboardView: {
209
+    width: 5,
210
+    height: 5,
211
+  },
212
+  testPickerContainer: {
213
+    flexDirection: 'row',
214
+    flexWrap: 'wrap',
215
+  },
216
+});

+ 55
- 0
example/android/app/_BUCK 查看文件

@@ -0,0 +1,55 @@
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
+load(":build_defs.bzl", "create_aar_targets", "create_jar_targets")
12
+
13
+lib_deps = []
14
+
15
+create_aar_targets(glob(["libs/*.aar"]))
16
+
17
+create_jar_targets(glob(["libs/*.jar"]))
18
+
19
+android_library(
20
+    name = "all-libs",
21
+    exported_deps = lib_deps,
22
+)
23
+
24
+android_library(
25
+    name = "app-code",
26
+    srcs = glob([
27
+        "src/main/java/**/*.java",
28
+    ]),
29
+    deps = [
30
+        ":all-libs",
31
+        ":build_config",
32
+        ":res",
33
+    ],
34
+)
35
+
36
+android_build_config(
37
+    name = "build_config",
38
+    package = "com.example",
39
+)
40
+
41
+android_resource(
42
+    name = "res",
43
+    package = "com.example",
44
+    res = "src/main/res",
45
+)
46
+
47
+android_binary(
48
+    name = "app",
49
+    keystore = "//android/keystores:debug",
50
+    manifest = "src/main/AndroidManifest.xml",
51
+    package_type = "debug",
52
+    deps = [
53
+        ":app-code",
54
+    ],
55
+)

+ 204
- 0
example/android/app/build.gradle 查看文件

@@ -0,0 +1,204 @@
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
+ *   // https://reactnative.dev/docs/performance#enable-the-ram-format
22
+ *   bundleCommand: "ram-bundle",
23
+ *
24
+ *   // whether to bundle JS and assets in debug mode
25
+ *   bundleInDebug: false,
26
+ *
27
+ *   // whether to bundle JS and assets in release mode
28
+ *   bundleInRelease: true,
29
+ *
30
+ *   // whether to bundle JS and assets in another build variant (if configured).
31
+ *   // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
32
+ *   // The configuration property can be in the following formats
33
+ *   //         'bundleIn${productFlavor}${buildType}'
34
+ *   //         'bundleIn${buildType}'
35
+ *   // bundleInFreeDebug: true,
36
+ *   // bundleInPaidRelease: true,
37
+ *   // bundleInBeta: true,
38
+ *
39
+ *   // whether to disable dev mode in custom build variants (by default only disabled in release)
40
+ *   // for example: to disable dev mode in the staging build type (if configured)
41
+ *   devDisabledInStaging: true,
42
+ *   // The configuration property can be in the following formats
43
+ *   //         'devDisabledIn${productFlavor}${buildType}'
44
+ *   //         'devDisabledIn${buildType}'
45
+ *
46
+ *   // the root of your project, i.e. where "package.json" lives
47
+ *   root: "../../",
48
+ *
49
+ *   // where to put the JS bundle asset in debug mode
50
+ *   jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
51
+ *
52
+ *   // where to put the JS bundle asset in release mode
53
+ *   jsBundleDirRelease: "$buildDir/intermediates/assets/release",
54
+ *
55
+ *   // where to put drawable resources / React Native assets, e.g. the ones you use via
56
+ *   // require('./image.png')), in debug mode
57
+ *   resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
58
+ *
59
+ *   // where to put drawable resources / React Native assets, e.g. the ones you use via
60
+ *   // require('./image.png')), in release mode
61
+ *   resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
62
+ *
63
+ *   // by default the gradle tasks are skipped if none of the JS files or assets change; this means
64
+ *   // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
65
+ *   // date; if you have any other folders that you want to ignore for performance reasons (gradle
66
+ *   // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
67
+ *   // for example, you might want to remove it from here.
68
+ *   inputExcludes: ["android/**", "ios/**"],
69
+ *
70
+ *   // override which node gets called and with what additional arguments
71
+ *   nodeExecutableAndArgs: ["node"],
72
+ *
73
+ *   // supply additional arguments to the packager
74
+ *   extraPackagerArgs: []
75
+ * ]
76
+ */
77
+
78
+project.ext.react = [
79
+    cliPath: "../../../node_modules/react-native/local-cli/cli.js",
80
+    entryFile: "./example/index.js",
81
+    root: "../../../",
82
+    enableHermes: false,  // clean and rebuild if changing
83
+]
84
+
85
+apply from: "../../../node_modules/react-native/react.gradle"
86
+
87
+/**
88
+ * Set this to true to create two separate APKs instead of one:
89
+ *   - An APK that only works on ARM devices
90
+ *   - An APK that only works on x86 devices
91
+ * The advantage is the size of the APK is reduced by about 4MB.
92
+ * Upload all the APKs to the Play Store and people will download
93
+ * the correct one based on the CPU architecture of their device.
94
+ */
95
+def enableSeparateBuildPerCPUArchitecture = false
96
+
97
+/**
98
+ * Run Proguard to shrink the Java bytecode in release builds.
99
+ */
100
+def enableProguardInReleaseBuilds = false
101
+
102
+/**
103
+ * The preferred build flavor of JavaScriptCore.
104
+ *
105
+ * For example, to use the international variant, you can use:
106
+ * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
107
+ *
108
+ * The international variant includes ICU i18n library and necessary data
109
+ * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
110
+ * give correct results when using with locales other than en-US.  Note that
111
+ * this variant is about 6MiB larger per architecture than default.
112
+ */
113
+def jscFlavor = 'org.webkit:android-jsc:+'
114
+
115
+/**
116
+ * Whether to enable the Hermes VM.
117
+ *
118
+ * This should be set on project.ext.react and mirrored here.  If it is not set
119
+ * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
120
+ * and the benefits of using Hermes will therefore be sharply reduced.
121
+ */
122
+def enableHermes = project.ext.react.get("enableHermes", false);
123
+
124
+android {
125
+    compileSdkVersion rootProject.ext.compileSdkVersion
126
+
127
+    compileOptions {
128
+        sourceCompatibility JavaVersion.VERSION_1_8
129
+        targetCompatibility JavaVersion.VERSION_1_8
130
+    }
131
+
132
+    defaultConfig {
133
+        applicationId "com.example"
134
+        minSdkVersion rootProject.ext.minSdkVersion
135
+        targetSdkVersion rootProject.ext.targetSdkVersion
136
+        versionCode 1
137
+        versionName "1.0"
138
+    }
139
+    splits {
140
+        abi {
141
+            reset()
142
+            enable enableSeparateBuildPerCPUArchitecture
143
+            universalApk false  // If true, also generate a universal APK
144
+            include "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
145
+        }
146
+    }
147
+    signingConfigs {
148
+        debug {
149
+            storeFile file('debug.keystore')
150
+            storePassword 'android'
151
+            keyAlias 'androiddebugkey'
152
+            keyPassword 'android'
153
+        }
154
+    }
155
+    buildTypes {
156
+        debug {
157
+            signingConfig signingConfigs.debug
158
+        }
159
+        release {
160
+            // Caution! In production, you need to generate your own keystore file.
161
+            // see https://reactnative.dev/docs/signed-apk-android.
162
+            signingConfig signingConfigs.debug
163
+            minifyEnabled enableProguardInReleaseBuilds
164
+            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
165
+        }
166
+    }
167
+    // applicationVariants are e.g. debug, release
168
+    applicationVariants.all { variant ->
169
+        variant.outputs.each { output ->
170
+            // For each separate APK per architecture, set a unique version code as described here:
171
+            // https://developer.android.com/studio/build/configure-apk-splits.html
172
+            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
173
+            def abi = output.getFilter(OutputFile.ABI)
174
+            if (abi != null) {  // null for the universal-debug, universal-release variants
175
+                output.versionCodeOverride =
176
+                        versionCodes.get(abi) * 1048576 + defaultConfig.versionCode
177
+            }
178
+
179
+        }
180
+    }
181
+}
182
+
183
+dependencies {
184
+    implementation project(':rnWebView')
185
+    implementation fileTree(dir: "libs", include: ["*.jar"])
186
+    implementation "com.facebook.react:react-native:+"  // From node_modules
187
+
188
+    if (enableHermes) {
189
+        def hermesPath = "../../../node_modules/hermes-engine/android/";
190
+        debugImplementation files(hermesPath + "hermes-debug.aar")
191
+        releaseImplementation files(hermesPath + "hermes-release.aar")
192
+    } else {
193
+        implementation jscFlavor
194
+    }
195
+}
196
+
197
+// Run this once to be able to run the application with BUCK
198
+// puts all compile dependencies into folder libs for BUCK to use
199
+task copyDownloadableDepsToLibs(type: Copy) {
200
+    from configurations.compile
201
+    into 'libs'
202
+}
203
+
204
+apply from: file("../../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

+ 19
- 0
example/android/app/build_defs.bzl 查看文件

@@ -0,0 +1,19 @@
1
+"""Helper definitions to glob .aar and .jar targets"""
2
+
3
+def create_aar_targets(aarfiles):
4
+    for aarfile in aarfiles:
5
+        name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")]
6
+        lib_deps.append(":" + name)
7
+        android_prebuilt_aar(
8
+            name = name,
9
+            aar = aarfile,
10
+        )
11
+
12
+def create_jar_targets(jarfiles):
13
+    for jarfile in jarfiles:
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
+        )

二進制
example/android/app/debug.keystore 查看文件


+ 10
- 0
example/android/app/proguard-rules.pro 查看文件

@@ -0,0 +1,10 @@
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:

+ 8
- 0
example/android/app/src/debug/AndroidManifest.xml 查看文件

@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+    xmlns:tools="http://schemas.android.com/tools">
4
+
5
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
6
+
7
+    <application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" />
8
+</manifest>

+ 27
- 0
example/android/app/src/main/AndroidManifest.xml 查看文件

@@ -0,0 +1,27 @@
1
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
2
+  package="com.example">
3
+
4
+    <uses-permission android:name="android.permission.INTERNET" />
5
+    <uses-permission android:name="android.permission.CAMERA" />
6
+
7
+    <application
8
+      android:name=".MainApplication"
9
+      android:label="@string/app_name"
10
+      android:icon="@mipmap/ic_launcher"
11
+      android:roundIcon="@mipmap/ic_launcher_round"
12
+      android:allowBackup="false"
13
+      android:theme="@style/AppTheme">
14
+      <activity
15
+        android:name=".MainActivity"
16
+        android:label="@string/app_name"
17
+        android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
18
+        android:windowSoftInputMode="adjustResize">
19
+        <intent-filter>
20
+            <action android:name="android.intent.action.MAIN" />
21
+            <category android:name="android.intent.category.LAUNCHER" />
22
+        </intent-filter>
23
+      </activity>
24
+      <activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
25
+    </application>
26
+
27
+</manifest>

+ 15
- 0
example/android/app/src/main/java/com/example/MainActivity.java 查看文件

@@ -0,0 +1,15 @@
1
+package com.example;
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. This is used to schedule
9
+   * rendering of the component.
10
+   */
11
+  @Override
12
+  protected String getMainComponentName() {
13
+    return "example";
14
+  }
15
+}

+ 76
- 0
example/android/app/src/main/java/com/example/MainApplication.java 查看文件

@@ -0,0 +1,76 @@
1
+package com.example;
2
+
3
+import android.app.Application;
4
+import android.content.Context;
5
+import com.facebook.react.PackageList;
6
+import com.facebook.react.ReactApplication;
7
+import com.facebook.react.ReactNativeHost;
8
+import com.facebook.react.ReactPackage;
9
+import com.facebook.soloader.SoLoader;
10
+import com.reactnativecommunity.webview.RNCWebViewPackage;
11
+import java.lang.reflect.InvocationTargetException;
12
+import java.util.List;
13
+
14
+public class MainApplication extends Application implements ReactApplication {
15
+
16
+  private final ReactNativeHost mReactNativeHost =
17
+      new ReactNativeHost(this) {
18
+        @Override
19
+        public boolean getUseDeveloperSupport() {
20
+          return BuildConfig.DEBUG;
21
+        }
22
+
23
+        @Override
24
+        protected List<ReactPackage> getPackages() {
25
+          @SuppressWarnings("UnnecessaryLocalVariable")
26
+          List<ReactPackage> packages = new PackageList(this).getPackages();
27
+          // Packages that cannot be autolinked yet can be added manually here, for example:
28
+          // packages.add(new MyReactNativePackage());
29
+          packages.add(new RNCWebViewPackage());
30
+          return packages;
31
+        }
32
+
33
+        @Override
34
+        protected String getJSMainModuleName() {
35
+          return "example/index";
36
+        }
37
+      };
38
+
39
+  @Override
40
+  public ReactNativeHost getReactNativeHost() {
41
+    return mReactNativeHost;
42
+  }
43
+
44
+  @Override
45
+  public void onCreate() {
46
+    super.onCreate();
47
+    SoLoader.init(this, /* native exopackage */ false);
48
+    initializeFlipper(this); // Remove this line if you don't want Flipper enabled
49
+  }
50
+
51
+  /**
52
+   * Loads Flipper in React Native templates.
53
+   *
54
+   * @param context
55
+   */
56
+  private static void initializeFlipper(Context context) {
57
+    if (BuildConfig.DEBUG) {
58
+      try {
59
+        /*
60
+         We use reflection here to pick up the class that initializes Flipper,
61
+        since Flipper library is not available in release mode
62
+        */
63
+        Class<?> aClass = Class.forName("com.facebook.flipper.ReactNativeFlipper");
64
+        aClass.getMethod("initializeFlipper", Context.class).invoke(null, context);
65
+      } catch (ClassNotFoundException e) {
66
+        e.printStackTrace();
67
+      } catch (NoSuchMethodException e) {
68
+        e.printStackTrace();
69
+      } catch (IllegalAccessException e) {
70
+        e.printStackTrace();
71
+      } catch (InvocationTargetException e) {
72
+        e.printStackTrace();
73
+      }
74
+    }
75
+  }
76
+}

二進制
example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png 查看文件


二進制
example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png 查看文件


二進制
example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png 查看文件


二進制
example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png 查看文件


二進制
example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png 查看文件


二進制
example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png 查看文件


二進制
example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png 查看文件


二進制
example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png 查看文件


二進制
example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png 查看文件


二進制
example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png 查看文件


+ 3
- 0
example/android/app/src/main/res/values/strings.xml 查看文件

@@ -0,0 +1,3 @@
1
+<resources>
2
+    <string name="app_name">example</string>
3
+</resources>

+ 9
- 0
example/android/app/src/main/res/values/styles.xml 查看文件

@@ -0,0 +1,9 @@
1
+<resources>
2
+
3
+    <!-- Base application theme. -->
4
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
5
+        <!-- Customize your theme here. -->
6
+        <item name="android:textColor">#000000</item>
7
+    </style>
8
+
9
+</resources>

+ 38
- 0
example/android/build.gradle 查看文件

@@ -0,0 +1,38 @@
1
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
2
+
3
+buildscript {
4
+    ext {
5
+        buildToolsVersion = "29.0.3"
6
+        minSdkVersion = 16
7
+        compileSdkVersion = 29
8
+        targetSdkVersion = 28
9
+    }
10
+    repositories {
11
+        google()
12
+        jcenter()
13
+    }
14
+    dependencies {
15
+        classpath("com.android.tools.build:gradle:3.5.2")
16
+
17
+        // NOTE: Do not place your application dependencies here; they belong
18
+        // in the individual module build.gradle files
19
+    }
20
+}
21
+
22
+allprojects {
23
+    repositories {
24
+        mavenLocal()
25
+        maven {
26
+            // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
27
+            url("$rootDir/../../node_modules/react-native/android")
28
+        }
29
+        maven {
30
+            // Android JSC is installed from npm
31
+            url("$rootDir/../../node_modules/jsc-android/dist")
32
+        }
33
+
34
+        google()
35
+        jcenter()
36
+        maven { url 'https://www.jitpack.io' }
37
+    }
38
+}

+ 21
- 0
example/android/gradle.properties 查看文件

@@ -0,0 +1,21 @@
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.useAndroidX=true
21
+android.enableJetifier=true

二進制
example/android/gradle/wrapper/gradle-wrapper.jar 查看文件


+ 5
- 0
example/android/gradle/wrapper/gradle-wrapper.properties 查看文件

@@ -0,0 +1,5 @@
1
+distributionBase=GRADLE_USER_HOME
2
+distributionPath=wrapper/dists
3
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-all.zip
4
+zipStoreBase=GRADLE_USER_HOME
5
+zipStorePath=wrapper/dists

+ 188
- 0
example/android/gradlew 查看文件

@@ -0,0 +1,188 @@
1
+#!/usr/bin/env sh
2
+
3
+#
4
+# Copyright 2015 the original author or authors.
5
+#
6
+# Licensed under the Apache License, Version 2.0 (the "License");
7
+# you may not use this file except in compliance with the License.
8
+# You may obtain a copy of the License at
9
+#
10
+#      https://www.apache.org/licenses/LICENSE-2.0
11
+#
12
+# Unless required by applicable law or agreed to in writing, software
13
+# distributed under the License is distributed on an "AS IS" BASIS,
14
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+# See the License for the specific language governing permissions and
16
+# limitations under the License.
17
+#
18
+
19
+##############################################################################
20
+##
21
+##  Gradle start up script for UN*X
22
+##
23
+##############################################################################
24
+
25
+# Attempt to set APP_HOME
26
+# Resolve links: $0 may be a link
27
+PRG="$0"
28
+# Need this for relative symlinks.
29
+while [ -h "$PRG" ] ; do
30
+    ls=`ls -ld "$PRG"`
31
+    link=`expr "$ls" : '.*-> \(.*\)$'`
32
+    if expr "$link" : '/.*' > /dev/null; then
33
+        PRG="$link"
34
+    else
35
+        PRG=`dirname "$PRG"`"/$link"
36
+    fi
37
+done
38
+SAVED="`pwd`"
39
+cd "`dirname \"$PRG\"`/" >/dev/null
40
+APP_HOME="`pwd -P`"
41
+cd "$SAVED" >/dev/null
42
+
43
+APP_NAME="Gradle"
44
+APP_BASE_NAME=`basename "$0"`
45
+
46
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48
+
49
+# Use the maximum available, or set MAX_FD != -1 to use that value.
50
+MAX_FD="maximum"
51
+
52
+warn () {
53
+    echo "$*"
54
+}
55
+
56
+die () {
57
+    echo
58
+    echo "$*"
59
+    echo
60
+    exit 1
61
+}
62
+
63
+# OS specific support (must be 'true' or 'false').
64
+cygwin=false
65
+msys=false
66
+darwin=false
67
+nonstop=false
68
+case "`uname`" in
69
+  CYGWIN* )
70
+    cygwin=true
71
+    ;;
72
+  Darwin* )
73
+    darwin=true
74
+    ;;
75
+  MINGW* )
76
+    msys=true
77
+    ;;
78
+  NONSTOP* )
79
+    nonstop=true
80
+    ;;
81
+esac
82
+
83
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84
+
85
+# Determine the Java command to use to start the JVM.
86
+if [ -n "$JAVA_HOME" ] ; then
87
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
88
+        # IBM's JDK on AIX uses strange locations for the executables
89
+        JAVACMD="$JAVA_HOME/jre/sh/java"
90
+    else
91
+        JAVACMD="$JAVA_HOME/bin/java"
92
+    fi
93
+    if [ ! -x "$JAVACMD" ] ; then
94
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
95
+
96
+Please set the JAVA_HOME variable in your environment to match the
97
+location of your Java installation."
98
+    fi
99
+else
100
+    JAVACMD="java"
101
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
102
+
103
+Please set the JAVA_HOME variable in your environment to match the
104
+location of your Java installation."
105
+fi
106
+
107
+# Increase the maximum file descriptors if we can.
108
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
109
+    MAX_FD_LIMIT=`ulimit -H -n`
110
+    if [ $? -eq 0 ] ; then
111
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
112
+            MAX_FD="$MAX_FD_LIMIT"
113
+        fi
114
+        ulimit -n $MAX_FD
115
+        if [ $? -ne 0 ] ; then
116
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
117
+        fi
118
+    else
119
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
120
+    fi
121
+fi
122
+
123
+# For Darwin, add options to specify how the application appears in the dock
124
+if $darwin; then
125
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
126
+fi
127
+
128
+# For Cygwin, switch paths to Windows format before running java
129
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
130
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
131
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
132
+    JAVACMD=`cygpath --unix "$JAVACMD"`
133
+
134
+    # We build the pattern for arguments to be converted via cygpath
135
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
136
+    SEP=""
137
+    for dir in $ROOTDIRSRAW ; do
138
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
139
+        SEP="|"
140
+    done
141
+    OURCYGPATTERN="(^($ROOTDIRS))"
142
+    # Add a user-defined pattern to the cygpath arguments
143
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
144
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
145
+    fi
146
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
147
+    i=0
148
+    for arg in "$@" ; do
149
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
150
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
151
+
152
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
153
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
154
+        else
155
+            eval `echo args$i`="\"$arg\""
156
+        fi
157
+        i=$((i+1))
158
+    done
159
+    case $i in
160
+        (0) set -- ;;
161
+        (1) set -- "$args0" ;;
162
+        (2) set -- "$args0" "$args1" ;;
163
+        (3) set -- "$args0" "$args1" "$args2" ;;
164
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
165
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
166
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
167
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
168
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
169
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
170
+    esac
171
+fi
172
+
173
+# Escape application args
174
+save () {
175
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
176
+    echo " "
177
+}
178
+APP_ARGS=$(save "$@")
179
+
180
+# Collect all arguments for the java command, following the shell quoting and substitution rules
181
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
182
+
183
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
184
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
185
+  cd "$(dirname "$0")"
186
+fi
187
+
188
+exec "$JAVACMD" "$@"

+ 100
- 0
example/android/gradlew.bat 查看文件

@@ -0,0 +1,100 @@
1
+@rem
2
+@rem Copyright 2015 the original author or authors.
3
+@rem
4
+@rem Licensed under the Apache License, Version 2.0 (the "License");
5
+@rem you may not use this file except in compliance with the License.
6
+@rem You may obtain a copy of the License at
7
+@rem
8
+@rem      http://www.apache.org/licenses/LICENSE-2.0
9
+@rem
10
+@rem Unless required by applicable law or agreed to in writing, software
11
+@rem distributed under the License is distributed on an "AS IS" BASIS,
12
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+@rem See the License for the specific language governing permissions and
14
+@rem limitations under the License.
15
+@rem
16
+
17
+@if "%DEBUG%" == "" @echo off
18
+@rem ##########################################################################
19
+@rem
20
+@rem  Gradle startup script for Windows
21
+@rem
22
+@rem ##########################################################################
23
+
24
+@rem Set local scope for the variables with windows NT shell
25
+if "%OS%"=="Windows_NT" setlocal
26
+
27
+set DIRNAME=%~dp0
28
+if "%DIRNAME%" == "" set DIRNAME=.
29
+set APP_BASE_NAME=%~n0
30
+set APP_HOME=%DIRNAME%
31
+
32
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
33
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
34
+
35
+@rem Find java.exe
36
+if defined JAVA_HOME goto findJavaFromJavaHome
37
+
38
+set JAVA_EXE=java.exe
39
+%JAVA_EXE% -version >NUL 2>&1
40
+if "%ERRORLEVEL%" == "0" goto init
41
+
42
+echo.
43
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
44
+echo.
45
+echo Please set the JAVA_HOME variable in your environment to match the
46
+echo location of your Java installation.
47
+
48
+goto fail
49
+
50
+:findJavaFromJavaHome
51
+set JAVA_HOME=%JAVA_HOME:"=%
52
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
53
+
54
+if exist "%JAVA_EXE%" goto init
55
+
56
+echo.
57
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
58
+echo.
59
+echo Please set the JAVA_HOME variable in your environment to match the
60
+echo location of your Java installation.
61
+
62
+goto fail
63
+
64
+:init
65
+@rem Get command-line arguments, handling Windows variants
66
+
67
+if not "%OS%" == "Windows_NT" goto win9xME_args
68
+
69
+:win9xME_args
70
+@rem Slurp the command line arguments.
71
+set CMD_LINE_ARGS=
72
+set _SKIP=2
73
+
74
+:win9xME_args_slurp
75
+if "x%~1" == "x" goto execute
76
+
77
+set CMD_LINE_ARGS=%*
78
+
79
+:execute
80
+@rem Setup the command line
81
+
82
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
83
+
84
+@rem Execute Gradle
85
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
86
+
87
+:end
88
+@rem End local scope for the variables with windows NT shell
89
+if "%ERRORLEVEL%"=="0" goto mainEnd
90
+
91
+:fail
92
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
93
+rem the _cmd.exe /c_ return code!
94
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
95
+exit /b 1
96
+
97
+:mainEnd
98
+if "%OS%"=="Windows_NT" endlocal
99
+
100
+:omega

+ 5
- 0
example/android/settings.gradle 查看文件

@@ -0,0 +1,5 @@
1
+rootProject.name = 'example'
2
+apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
3
+include ':app'
4
+include ':rnWebView'
5
+project(':rnWebView').projectDir = new File(rootProject.projectDir, '../../android')

+ 4
- 0
example/app.json 查看文件

@@ -0,0 +1,4 @@
1
+{
2
+  "name": "example",
3
+  "displayName": "example"
4
+}

+ 9
- 0
example/assets/test.html 查看文件

@@ -0,0 +1,9 @@
1
+<!doctype html>
2
+<html>
3
+    <head>
4
+        <title>Test</title>
5
+    </head>
6
+    <body>
7
+        <h2>Local page test</h2>
8
+    </body>
9
+</html>

+ 3
- 0
example/babel.config.js 查看文件

@@ -0,0 +1,3 @@
1
+module.exports = {
2
+  presets: ['module:metro-react-native-babel-preset'],
3
+};

+ 72
- 0
example/examples/Alerts.tsx 查看文件

@@ -0,0 +1,72 @@
1
+import React, {Component} from 'react';
2
+import {Text, View} from 'react-native';
3
+
4
+import WebView from 'react-native-webview';
5
+
6
+const HTML = `
7
+<!DOCTYPE html>\n
8
+<html>
9
+  <head>
10
+    <title>Alerts</title>
11
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+    <meta name="viewport" content="width=320, user-scalable=no">
13
+    <style type="text/css">
14
+      body {
15
+        margin: 0;
16
+        padding: 0;
17
+        font: 62.5% arial, sans-serif;
18
+        background: #ccc;
19
+      }
20
+    </style>
21
+  </head>
22
+  <body>
23
+    <button onclick="showAlert()">Show alert</button>
24
+    <button onclick="showConfirm()">Show confirm</button>
25
+    <button onclick="showPrompt()">Show prompt</button>
26
+    <p id="demo"></p>    
27
+    <script>
28
+      function showAlert() {
29
+        alert("Hello! I am an alert box!");
30
+        document.getElementById("demo").innerHTML = "Alert dismissed!";
31
+      }
32
+      function showConfirm() {
33
+        var response;
34
+        if (confirm("Press a button!")) {
35
+          response = "You pressed OK on confirm!";
36
+        } else {
37
+          response = "You pressed Cancel on confirm!";
38
+        }
39
+        document.getElementById("demo").innerHTML = response;
40
+      }
41
+      function showPrompt() {
42
+        var message;
43
+        const name = prompt("Please enter your name", "Name");
44
+        if (name !== null) {
45
+          message = "Hello " + name;
46
+        } else {
47
+          message = "You pressed Cancel on prompt!";
48
+        }
49
+        document.getElementById("demo").innerHTML = message;
50
+      }
51
+    </script>
52
+  </body>
53
+</html>
54
+`;
55
+
56
+type Props = {};
57
+type State = {};
58
+
59
+export default class Alerts extends Component<Props, State> {
60
+  state = {};
61
+
62
+  render() {
63
+    return (
64
+      <View style={{ height: 120 }}>
65
+        <WebView
66
+          source={{html: HTML}}
67
+          automaticallyAdjustContentInsets={false}
68
+        />
69
+      </View>
70
+    );
71
+  }
72
+}

+ 54
- 0
example/examples/Background.tsx 查看文件

@@ -0,0 +1,54 @@
1
+import React, {Component} from 'react';
2
+import {Text, View} from 'react-native';
3
+
4
+import WebView from 'react-native-webview';
5
+
6
+const HTML = `
7
+<!DOCTYPE html>\n
8
+<html>
9
+  <head>
10
+    <title>Hello World</title>
11
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+    <meta name="viewport" content="width=320, user-scalable=no">
13
+    <style type="text/css">
14
+      body {
15
+        margin: 0;
16
+        padding: 0;
17
+        font: 62.5% arial, sans-serif;
18
+        background: transparent;
19
+      }
20
+    </style>
21
+  </head>
22
+  <body>
23
+    <p>HTML content in transparent body.</p>
24
+  </body>
25
+</html>
26
+`;
27
+
28
+type Props = {};
29
+type State = {
30
+  backgroundColor: string,
31
+};
32
+
33
+export default class Background extends Component<Props, State> {
34
+  state = {
35
+    backgroundColor: '#FF00FF00'
36
+  };
37
+
38
+  render() {
39
+    return (
40
+      <View>
41
+        <View style={{backgroundColor:'red'}}>
42
+          <View style={{ height: 120 }}>
43
+            <WebView
44
+              source={{html: HTML}}
45
+              automaticallyAdjustContentInsets={false}
46
+              style={{backgroundColor:'#00000000'}}
47
+            />
48
+          </View>
49
+        </View>
50
+        <Text>WebView is transparent contained in a View with a red backgroundColor</Text>
51
+      </View>
52
+    );
53
+  }
54
+}

+ 55
- 0
example/examples/Downloads.tsx 查看文件

@@ -0,0 +1,55 @@
1
+import React, {Component} from 'react';
2
+import {Alert, Platform, View} from 'react-native';
3
+
4
+import WebView, {FileDownload} from 'react-native-webview';
5
+
6
+const HTML = `
7
+<!DOCTYPE html>\n
8
+<html>
9
+  <head>
10
+    <title>Downloads</title>
11
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+    <meta name="viewport" content="width=320, user-scalable=no">
13
+    <style type="text/css">
14
+      body {
15
+        margin: 0;
16
+        padding: 0;
17
+        font: 62.5% arial, sans-serif;
18
+        background: #ccc;
19
+      }
20
+    </style>
21
+  </head>
22
+  <body>
23
+    <a href="https://www.7-zip.org/a/7za920.zip">Example zip file download</a>
24
+  </body>
25
+</html>
26
+`;
27
+
28
+type Props = {};
29
+type State = {};
30
+
31
+export default class Downloads extends Component<Props, State> {
32
+  state = {};
33
+
34
+  onFileDownload = ({ nativeEvent }: { nativeEvent: FileDownload } ) => {
35
+    Alert.alert("File download detected", nativeEvent.downloadUrl);
36
+  };
37
+
38
+  render() {
39
+    const platformProps = Platform.select({
40
+      ios: {
41
+        onFileDownload: this.onFileDownload,
42
+      },
43
+    });
44
+
45
+    return (
46
+      <View style={{ height: 120 }}>
47
+        <WebView
48
+          source={{html: HTML}}
49
+          automaticallyAdjustContentInsets={false}
50
+          {...platformProps}
51
+        />
52
+      </View>
53
+    );
54
+  }
55
+}

+ 160
- 0
example/examples/Injection.tsx 查看文件

@@ -0,0 +1,160 @@
1
+import React, {Component} from 'react';
2
+import {Text, View, ScrollView} from 'react-native';
3
+
4
+import WebView from 'react-native-webview';
5
+
6
+// const HTML = `
7
+// <!DOCTYPE html>
8
+// <html>
9
+//   <head>
10
+//       <meta charset="utf-8">
11
+//       <meta name="viewport" content="width=device-width, initial-scale=1">
12
+//       <title>iframe test</title>
13
+//   </head>
14
+//   <body>
15
+//     <p style="">beforeContentLoaded on the top frame <span id="before_failed" style="display: inline-block;">failed</span><span id="before_succeeded" style="display: none;">succeeded</span>!</p>
16
+//     <p style="">afterContentLoaded on the top frame <span id="after_failed" style="display: inline-block;">failed</span><span id="after_succeeded" style="display: none;">succeeded</span>!</p>
17
+//     <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe.html?v=1" name="iframe_0" style="width: 100%; height: 25px;"></iframe>
18
+//     <iframe src="https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframe2.html?v=1" name="iframe_1" style="width: 100%; height: 25px;"></iframe>
19
+//     <iframe src="https://www.ebay.co.uk" name="iframe_2" style="width: 100%; height: 25px;"></iframe>
20
+//   </body>
21
+// </html>
22
+// `;
23
+
24
+type Props = {};
25
+type State = {
26
+  backgroundColor: string,
27
+};
28
+
29
+export default class Injection extends Component<Props, State> {
30
+  state = {
31
+    backgroundColor: '#FF00FF00'
32
+  };
33
+
34
+  render() {
35
+    return (
36
+      <ScrollView>
37
+        <View style={{ }}>
38
+          <View style={{ height: 300 }}>
39
+            <WebView
40
+              /**
41
+               * This HTML is a copy of a multi-frame JS injection test that I had lying around.
42
+               * @see https://birchlabs.co.uk/linguabrowse/infopages/obsol/iframeTest.html
43
+               */
44
+              // source={{ html: HTML }}
45
+              source={{ uri: "https://birchlabs.co.uk/linguabrowse/infopages/obsol/rnw_iframe_test.html" }}
46
+              automaticallyAdjustContentInsets={false}
47
+              style={{backgroundColor:'#00000000'}}
48
+              
49
+              /* Must be populated in order for `messagingEnabled` to be `true` to activate the
50
+               * JS injection user scripts, consistent with current behaviour. This is undesirable,
51
+               * so needs addressing in a follow-up PR. */
52
+              onMessage={() => {}}
53
+
54
+              /* We set this property in each frame */
55
+              injectedJavaScriptBeforeContentLoaded={`
56
+              console.log("executing injectedJavaScriptBeforeContentLoaded...");
57
+              if(typeof window.top.injectedIframesBeforeContentLoaded === "undefined"){
58
+                window.top.injectedIframesBeforeContentLoaded = [];
59
+              }
60
+              window.self.colourToUse = "orange";
61
+              if(window.self === window.top){
62
+                console.log("Was window.top. window.frames.length is:", window.frames.length);
63
+                window.self.numberOfFramesAtBeforeContentLoaded = window.frames.length;
64
+                function declareSuccessOfBeforeContentLoaded(head){
65
+                  var style = window.self.document.createElement('style');
66
+                  style.type = 'text/css';
67
+                  style.innerHTML = "#before_failed { display: none !important; }#before_succeeded { display: inline-block !important; }";
68
+                  head.appendChild(style);
69
+                }
70
+
71
+                const head = (window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
72
+
73
+                if(head){
74
+                  declareSuccessOfBeforeContentLoaded(head);
75
+                } else {
76
+                  window.self.document.addEventListener("DOMContentLoaded", function (event) {
77
+                    const head = (window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
78
+                    declareSuccessOfBeforeContentLoaded(head);
79
+                  });
80
+                }
81
+              } else {
82
+                window.top.injectedIframesBeforeContentLoaded.push(window.self.name);
83
+                console.log("wasn't window.top.");
84
+                console.log("wasn't window.top. Still going...");
85
+              }
86
+              `}
87
+              
88
+              injectedJavaScriptForMainFrameOnly={false}
89
+
90
+              /* We read the colourToUse property in each frame to recolour each frame */
91
+              injectedJavaScript={`
92
+              console.log("executing injectedJavaScript...");
93
+              if(typeof window.top.injectedIframesAfterContentLoaded === "undefined"){
94
+                window.top.injectedIframesAfterContentLoaded = [];
95
+              }
96
+
97
+              if(window.self.colourToUse){
98
+                window.self.document.body.style.backgroundColor = window.self.colourToUse;
99
+              } else {
100
+                window.self.document.body.style.backgroundColor = "cyan";
101
+              }
102
+
103
+              if(window.self === window.top){
104
+                function declareSuccessOfAfterContentLoaded(head){
105
+                  var style = window.self.document.createElement('style');
106
+                  style.type = 'text/css';
107
+                  style.innerHTML = "#after_failed { display: none !important; }#after_succeeded { display: inline-block !important; }";
108
+                  head.appendChild(style);
109
+                }
110
+
111
+                declareSuccessOfAfterContentLoaded(window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
112
+
113
+                // var numberOfFramesAtBeforeContentLoadedEle = document.createElement('p');
114
+                // numberOfFramesAtBeforeContentLoadedEle.textContent = "Number of iframes upon the main frame's beforeContentLoaded: " +
115
+                // window.self.numberOfFramesAtBeforeContentLoaded;
116
+
117
+                // var numberOfFramesAtAfterContentLoadedEle = document.createElement('p');
118
+                // numberOfFramesAtAfterContentLoadedEle.textContent = "Number of iframes upon the main frame's afterContentLoaded: " + window.frames.length;
119
+                // numberOfFramesAtAfterContentLoadedEle.id = "numberOfFramesAtAfterContentLoadedEle";
120
+
121
+                var namedFramesAtBeforeContentLoadedEle = document.createElement('p');
122
+                namedFramesAtBeforeContentLoadedEle.textContent = "Names of iframes that called beforeContentLoaded: " + JSON.stringify(window.top.injectedIframesBeforeContentLoaded);
123
+                namedFramesAtBeforeContentLoadedEle.id = "namedFramesAtBeforeContentLoadedEle";
124
+
125
+                var namedFramesAtAfterContentLoadedEle = document.createElement('p');
126
+                namedFramesAtAfterContentLoadedEle.textContent = "Names of iframes that called afterContentLoaded: " + JSON.stringify(window.top.injectedIframesAfterContentLoaded);
127
+                namedFramesAtAfterContentLoadedEle.id = "namedFramesAtAfterContentLoadedEle";
128
+
129
+                // document.body.appendChild(numberOfFramesAtBeforeContentLoadedEle);
130
+                // document.body.appendChild(numberOfFramesAtAfterContentLoadedEle);
131
+                document.body.appendChild(namedFramesAtBeforeContentLoadedEle);
132
+                document.body.appendChild(namedFramesAtAfterContentLoadedEle);
133
+              } else {
134
+                window.top.injectedIframesAfterContentLoaded.push(window.self.name);
135
+                window.top.document.getElementById('namedFramesAtAfterContentLoadedEle').textContent = "Names of iframes that called afterContentLoaded: " + JSON.stringify(window.top.injectedIframesAfterContentLoaded);
136
+              }
137
+              `}
138
+            />
139
+          </View>
140
+        </View>
141
+        <Text>This test presents three iframes: iframe_0 (yellow); iframe_1 (pink); and iframe_2 (transparent, because its 'X-Frame-Options' is set to 'SAMEORIGIN').</Text>
142
+        <Text>Before injection, the main frame's background is the browser's default value (transparent or white) and each frame has its natural colour.</Text>
143
+        {/*<Text>1a) At injection time "beforeContentLoaded", a variable will be set in each frame to set 'orange' as the "colour to be used".</Text>*/}
144
+        {/*<Text>1b) Also upon "beforeContentLoaded", a style element to change the text "beforeContentLoaded failed" -> "beforeContentLoaded succeeded" will be applied as soon as the head has loaded.</Text>*/}
145
+        {/*<Text>2a) At injection time "afterContentLoaded", that variable will be read – if present, the colour orange will be injected into all frames. Otherwise, cyan.</Text>*/}
146
+        {/*<Text>2b) Also upon "afterContentLoaded", a style element to change the text "afterContentLoaded failed" -> "afterContentLoaded succeeded" will be applied as soon as the head has loaded.</Text>*/}
147
+        <Text>✅ If the main frame becomes orange, then top-frame injection both beforeContentLoaded and afterContentLoaded is supported.</Text>
148
+        <Text>✅ If iframe_0, and iframe_1 become orange, then multi-frame injection beforeContentLoaded and afterContentLoaded is supported.</Text>
149
+        <Text>✅ If the two texts say "beforeContentLoaded on the top frame succeeded!" and "afterContentLoaded on the top frame succeeded!", then both injection times are supported at least on the main frame.</Text>
150
+        <Text>⚠️ If either of the two iframes become coloured cyan, then for that given frame, JS injection succeeded after the content loaded, but didn't occur before the content loaded - please note that for iframes, this may not be a test failure, as it is not clear whether we would expect iframes to support an injection time of beforeContentLoaded anyway.</Text>
151
+        <Text>⚠️ If "Names of iframes that called beforeContentLoaded: " is [], then see above.</Text>
152
+        <Text>❌ If "Names of iframes that called afterContentLoaded: " is [], then afterContentLoaded is not supported in iframes.</Text>
153
+        <Text>❌ If the main frame becomes coloured cyan, then JS injection succeeded after the content loaded, but didn't occur before the content loaded.</Text>
154
+        <Text>❌ If the text "beforeContentLoaded on the top frame failed" remains unchanged, then JS injection has failed on the main frame before the content loaded.</Text>
155
+        <Text>❌ If the text "afterContentLoaded on the top frame failed" remains unchanged, then JS injection has failed on the main frame after the content loaded.</Text>
156
+        <Text>❌ If the iframes remain their original colours (yellow and pink), then multi-frame injection is not supported at all.</Text>
157
+      </ScrollView>
158
+    );
159
+  }
160
+}

+ 16
- 0
example/examples/LocalPageLoad.tsx 查看文件

@@ -0,0 +1,16 @@
1
+import React, {Component} from 'react';
2
+import {View, Text, Alert, TextInput, Button} from 'react-native';
3
+import WebView from 'react-native-webview';
4
+const localHtmlFile = require('../assets/test.html');
5
+
6
+export default class LocalPageLoad extends Component<Props, State> {
7
+    render() {
8
+      return (
9
+        <View>
10
+            <View style={{ width: '100%', height: '100%' }}>
11
+                <WebView source={localHtmlFile}/>
12
+          </View>
13
+        </View>
14
+      );
15
+    }
16
+  }

+ 68
- 0
example/examples/Scrolling.tsx 查看文件

@@ -0,0 +1,68 @@
1
+import React, {Component} from 'react';
2
+import {Button, Text, View} from 'react-native';
3
+
4
+import WebView from 'react-native-webview';
5
+
6
+const HTML = `
7
+<!DOCTYPE html>\n
8
+<html>
9
+  <head>
10
+    <title>Hello World</title>
11
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+    <meta name="viewport" content="width=320, user-scalable=no">
13
+    <style type="text/css">
14
+      body {
15
+        margin: 0;
16
+        padding: 0;
17
+        font: 62.5% arial, sans-serif;
18
+        background: #ccc;
19
+      }
20
+    </style>
21
+  </head>
22
+  <body>
23
+    <p>Lorem ipsum dolor sit amet, virtute utroque voluptaria et duo, probo aeque partiendo pri at. Mea ut stet aliquip deterruisset. Inani erroribus principes ei mel, no dicit recteque delicatissimi usu. Ne has dolore nominavi, feugait hendrerit interesset vis ea, amet regione ne pri. Te cum amet etiam.</p>
24
+    <p>Ut adipiscing neglegentur mediocritatem sea, suas abhorreant ius cu, ne nostrud feugiat per. Nam paulo facete iudicabit an, an brute mundi suavitate has, ex utamur numquam duo. Sea platonem argumentum instructior in, quo no prima inani perfecto. Ex illum postea copiosae has, ei mea sonet ocurreret.</p>
25
+    <p>Has convenire erroribus cu, quo homero facilisis inciderint ea. Vix choro gloriatur definitionem an, te exerci debitis voluptaria pri, mea admodum antiopam neglegentur te. His ea iisque splendide, nam id malorum pertinacia. Iusto tempor in eos, vis debet erant an. An nostrum rationibus sit, et sed dicta delenit suscipiantur. Est dolore vituperatoribus in, ubique explicari est cu. Legere tractatos ut usu, probo atqui vituperata in usu, mazim nemore praesent pro no.</p>
26
+    <p>Ei pri facilisi accusamus. Ut partem quaestio sit, an usu audiam quaerendum, ei qui hinc soleat. Fabulas phaedrum erroribus ut est. Intellegebat delicatissimi vis cu. His ea vidit libris facilis. Usu ne scripta legimus intellegam. Hendrerit urbanitas accommodare mei in.</p>
27
+    <p>Brute appetere efficiendi has ne. Ei ornatus labores vis. Vel harum fierent no, ad erat partiendo vis, harum democritum duo at. Has no labitur vulputate. Has cu autem aperiam hendrerit, sed eu justo verear menandri.</p>
28
+  </body>
29
+</html>
30
+`;
31
+
32
+type Props = {};
33
+type State = {
34
+  scrollEnabled: boolean,
35
+  lastScrollEvent: string
36
+};
37
+
38
+export default class Scrolling extends Component<Props, State> {
39
+  state = {
40
+    scrollEnabled: true,
41
+    lastScrollEvent: ''
42
+  };
43
+
44
+  render() {
45
+    return (
46
+      <View>
47
+        <View style={{ height: 120 }}>
48
+          <WebView
49
+            source={{html: HTML}}
50
+            automaticallyAdjustContentInsets={false}
51
+            onScroll={this._onScroll}
52
+            scrollEnabled={this.state.scrollEnabled}
53
+          />
54
+        </View>
55
+        <Button
56
+          title={this.state.scrollEnabled ? 'Scroll enabled' : 'Scroll disabled'}
57
+          onPress={() => this.setState({scrollEnabled: !this.state.scrollEnabled})}
58
+        />
59
+        <Text>Last scroll event:</Text>
60
+        <Text>{this.state.lastScrollEvent}</Text>
61
+      </View>
62
+    );
63
+  }
64
+
65
+  _onScroll = event => {
66
+    this.setState({lastScrollEvent: JSON.stringify(event.nativeEvent)});
67
+  }
68
+}

+ 69
- 0
example/examples/Uploads.tsx 查看文件

@@ -0,0 +1,69 @@
1
+import React, {Component} from 'react';
2
+import {Button, Linking, Text, View} from 'react-native';
3
+
4
+import WebView from 'react-native-webview';
5
+
6
+const HTML = `
7
+<!DOCTYPE html>\n
8
+<html>
9
+  <head>
10
+    <title>Uploads</title>
11
+    <meta http-equiv="content-type" content="text/html; charset=utf-8">
12
+    <meta name="viewport" content="width=320, user-scalable=no">
13
+    <style type="text/css">
14
+      body {
15
+        margin: 0;
16
+        padding: 0;
17
+        font: 62.5% arial, sans-serif;
18
+        background: #ccc;
19
+      }
20
+    </style>
21
+  </head>
22
+  <body>
23
+    <p>
24
+      <label for="images-only">Images only file upload</label>
25
+      <input name="images-only" type="file" accept="image/*">
26
+    </p>
27
+    <p>
28
+      <label for="video-only">Video only file upload</label>
29
+      <input name="video-only" type="file" accept="video/*">
30
+    </p>
31
+    <p>
32
+      <label for="any-file">Any file upload</label>
33
+      <input name="any-file" type="file">
34
+    </p>
35
+  </body>
36
+</html>
37
+`;
38
+
39
+type Props = {};
40
+type State = {};
41
+
42
+export default class Uploads extends Component<Props, State> {
43
+  state = {};
44
+
45
+  render() {
46
+    return (
47
+      <View>
48
+        <View style={{ height: 120 }}>
49
+          <WebView
50
+            source={{html: HTML}}
51
+            automaticallyAdjustContentInsets={false}
52
+          />
53
+        </View>
54
+        <Text>
55
+            Android limitation: If the file input should show camera options for the user,
56
+            and the app has the ability to request the camera permission, then the user must
57
+            grant permission first in order to see the options. Since this example app does
58
+            have the permission declared, you must allow it in settings to be able to see
59
+            camera options. If your app does not have the camera permission declared, then
60
+            there is no restriction to showing the camera options.
61
+        </Text>
62
+        <Button
63
+          title="Open settings"
64
+          onPress={() => Linking.openSettings()}
65
+        />
66
+      </View>
67
+    );
68
+  }
69
+}

+ 9
- 0
example/index.js 查看文件

@@ -0,0 +1,9 @@
1
+/**
2
+ * @format
3
+ */
4
+
5
+import {AppRegistry} from 'react-native';
6
+import App from './App';
7
+import {name as appName} from './app.json';
8
+
9
+AppRegistry.registerComponent(appName, () => App);

+ 56
- 0
example/ios/Podfile 查看文件

@@ -0,0 +1,56 @@
1
+platform :ios, '9.0'
2
+require_relative '../../node_modules/@react-native-community/cli-platform-ios/native_modules'
3
+
4
+project './example.xcodeproj'
5
+
6
+target 'example' do
7
+  use_native_modules!
8
+
9
+  pod 'react-native-webview', :path => "../.."
10
+
11
+  pod 'FBLazyVector', :path => "../../node_modules/react-native/Libraries/FBLazyVector"
12
+  pod 'FBReactNativeSpec', :path => "../../node_modules/react-native/Libraries/FBReactNativeSpec"
13
+  pod 'RCTRequired', :path => "../../node_modules/react-native/Libraries/RCTRequired"
14
+  pod 'RCTTypeSafety', :path => "../../node_modules/react-native/Libraries/TypeSafety"
15
+  pod 'React', :path => '../../node_modules/react-native/'
16
+  pod 'React-Core', :path => '../../node_modules/react-native/'
17
+  pod 'React-CoreModules', :path => '../../node_modules/react-native/React/CoreModules'
18
+  pod 'React-Core/DevSupport', :path => '../../node_modules/react-native/'
19
+  pod 'React-RCTActionSheet', :path => '../../node_modules/react-native/Libraries/ActionSheetIOS'
20
+  pod 'React-RCTAnimation', :path => '../../node_modules/react-native/Libraries/NativeAnimation'
21
+  pod 'React-RCTBlob', :path => '../../node_modules/react-native/Libraries/Blob'
22
+  pod 'React-RCTImage', :path => '../../node_modules/react-native/Libraries/Image'
23
+  pod 'React-RCTLinking', :path => '../../node_modules/react-native/Libraries/LinkingIOS'
24
+  pod 'React-RCTNetwork', :path => '../../node_modules/react-native/Libraries/Network'
25
+  pod 'React-RCTSettings', :path => '../../node_modules/react-native/Libraries/Settings'
26
+  pod 'React-RCTText', :path => '../../node_modules/react-native/Libraries/Text'
27
+  pod 'React-RCTVibration', :path => '../../node_modules/react-native/Libraries/Vibration'
28
+  pod 'React-Core/RCTWebSocket', :path => '../../node_modules/react-native/'
29
+
30
+  pod 'React-cxxreact', :path => '../../node_modules/react-native/ReactCommon/cxxreact'
31
+  pod 'React-jsi', :path => '../../node_modules/react-native/ReactCommon/jsi'
32
+  pod 'React-jsiexecutor', :path => '../../node_modules/react-native/ReactCommon/jsiexecutor'
33
+  pod 'React-jsinspector', :path => '../../node_modules/react-native/ReactCommon/jsinspector'
34
+  pod 'ReactCommon/callinvoker', :path => "../../node_modules/react-native/ReactCommon"
35
+  pod 'ReactCommon/turbomodule/core', :path => "../../node_modules/react-native/ReactCommon"
36
+  pod 'Yoga', :path => '../../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
37
+
38
+  pod 'DoubleConversion', :podspec => '../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
39
+  pod 'glog', :podspec => '../../node_modules/react-native/third-party-podspecs/glog.podspec'
40
+  pod 'Folly', :podspec => '../../node_modules/react-native/third-party-podspecs/Folly.podspec'
41
+
42
+  target 'exampleTests' do
43
+    inherit! :search_paths
44
+    # Pods for testing
45
+  end
46
+end
47
+
48
+target 'example-tvOS' do
49
+  # Pods for example-tvOS
50
+
51
+  target 'example-tvOSTests' do
52
+    inherit! :complete
53
+    # Pods for testing
54
+  end
55
+
56
+end

+ 368
- 0
example/ios/Podfile.lock 查看文件

@@ -0,0 +1,368 @@
1
+PODS:
2
+  - boost-for-react-native (1.63.0)
3
+  - DoubleConversion (1.1.6)
4
+  - FBLazyVector (0.62.2)
5
+  - FBReactNativeSpec (0.62.2):
6
+    - Folly (= 2018.10.22.00)
7
+    - RCTRequired (= 0.62.2)
8
+    - RCTTypeSafety (= 0.62.2)
9
+    - React-Core (= 0.62.2)
10
+    - React-jsi (= 0.62.2)
11
+    - ReactCommon/turbomodule/core (= 0.62.2)
12
+  - Folly (2018.10.22.00):
13
+    - boost-for-react-native
14
+    - DoubleConversion
15
+    - Folly/Default (= 2018.10.22.00)
16
+    - glog
17
+  - Folly/Default (2018.10.22.00):
18
+    - boost-for-react-native
19
+    - DoubleConversion
20
+    - glog
21
+  - glog (0.3.5)
22
+  - RCTRequired (0.62.2)
23
+  - RCTTypeSafety (0.62.2):
24
+    - FBLazyVector (= 0.62.2)
25
+    - Folly (= 2018.10.22.00)
26
+    - RCTRequired (= 0.62.2)
27
+    - React-Core (= 0.62.2)
28
+  - React (0.62.2):
29
+    - React-Core (= 0.62.2)
30
+    - React-Core/DevSupport (= 0.62.2)
31
+    - React-Core/RCTWebSocket (= 0.62.2)
32
+    - React-RCTActionSheet (= 0.62.2)
33
+    - React-RCTAnimation (= 0.62.2)
34
+    - React-RCTBlob (= 0.62.2)
35
+    - React-RCTImage (= 0.62.2)
36
+    - React-RCTLinking (= 0.62.2)
37
+    - React-RCTNetwork (= 0.62.2)
38
+    - React-RCTSettings (= 0.62.2)
39
+    - React-RCTText (= 0.62.2)
40
+    - React-RCTVibration (= 0.62.2)
41
+  - React-Core (0.62.2):
42
+    - Folly (= 2018.10.22.00)
43
+    - glog
44
+    - React-Core/Default (= 0.62.2)
45
+    - React-cxxreact (= 0.62.2)
46
+    - React-jsi (= 0.62.2)
47
+    - React-jsiexecutor (= 0.62.2)
48
+    - Yoga
49
+  - React-Core/CoreModulesHeaders (0.62.2):
50
+    - Folly (= 2018.10.22.00)
51
+    - glog
52
+    - React-Core/Default
53
+    - React-cxxreact (= 0.62.2)
54
+    - React-jsi (= 0.62.2)
55
+    - React-jsiexecutor (= 0.62.2)
56
+    - Yoga
57
+  - React-Core/Default (0.62.2):
58
+    - Folly (= 2018.10.22.00)
59
+    - glog
60
+    - React-cxxreact (= 0.62.2)
61
+    - React-jsi (= 0.62.2)
62
+    - React-jsiexecutor (= 0.62.2)
63
+    - Yoga
64
+  - React-Core/DevSupport (0.62.2):
65
+    - Folly (= 2018.10.22.00)
66
+    - glog
67
+    - React-Core/Default (= 0.62.2)
68
+    - React-Core/RCTWebSocket (= 0.62.2)
69
+    - React-cxxreact (= 0.62.2)
70
+    - React-jsi (= 0.62.2)
71
+    - React-jsiexecutor (= 0.62.2)
72
+    - React-jsinspector (= 0.62.2)
73
+    - Yoga
74
+  - React-Core/RCTActionSheetHeaders (0.62.2):
75
+    - Folly (= 2018.10.22.00)
76
+    - glog
77
+    - React-Core/Default
78
+    - React-cxxreact (= 0.62.2)
79
+    - React-jsi (= 0.62.2)
80
+    - React-jsiexecutor (= 0.62.2)
81
+    - Yoga
82
+  - React-Core/RCTAnimationHeaders (0.62.2):
83
+    - Folly (= 2018.10.22.00)
84
+    - glog
85
+    - React-Core/Default
86
+    - React-cxxreact (= 0.62.2)
87
+    - React-jsi (= 0.62.2)
88
+    - React-jsiexecutor (= 0.62.2)
89
+    - Yoga
90
+  - React-Core/RCTBlobHeaders (0.62.2):
91
+    - Folly (= 2018.10.22.00)
92
+    - glog
93
+    - React-Core/Default
94
+    - React-cxxreact (= 0.62.2)
95
+    - React-jsi (= 0.62.2)
96
+    - React-jsiexecutor (= 0.62.2)
97
+    - Yoga
98
+  - React-Core/RCTImageHeaders (0.62.2):
99
+    - Folly (= 2018.10.22.00)
100
+    - glog
101
+    - React-Core/Default
102
+    - React-cxxreact (= 0.62.2)
103
+    - React-jsi (= 0.62.2)
104
+    - React-jsiexecutor (= 0.62.2)
105
+    - Yoga
106
+  - React-Core/RCTLinkingHeaders (0.62.2):
107
+    - Folly (= 2018.10.22.00)
108
+    - glog
109
+    - React-Core/Default
110
+    - React-cxxreact (= 0.62.2)
111
+    - React-jsi (= 0.62.2)
112
+    - React-jsiexecutor (= 0.62.2)
113
+    - Yoga
114
+  - React-Core/RCTNetworkHeaders (0.62.2):
115
+    - Folly (= 2018.10.22.00)
116
+    - glog
117
+    - React-Core/Default
118
+    - React-cxxreact (= 0.62.2)
119
+    - React-jsi (= 0.62.2)
120
+    - React-jsiexecutor (= 0.62.2)
121
+    - Yoga
122
+  - React-Core/RCTSettingsHeaders (0.62.2):
123
+    - Folly (= 2018.10.22.00)
124
+    - glog
125
+    - React-Core/Default
126
+    - React-cxxreact (= 0.62.2)
127
+    - React-jsi (= 0.62.2)
128
+    - React-jsiexecutor (= 0.62.2)
129
+    - Yoga
130
+  - React-Core/RCTTextHeaders (0.62.2):
131
+    - Folly (= 2018.10.22.00)
132
+    - glog
133
+    - React-Core/Default
134
+    - React-cxxreact (= 0.62.2)
135
+    - React-jsi (= 0.62.2)
136
+    - React-jsiexecutor (= 0.62.2)
137
+    - Yoga
138
+  - React-Core/RCTVibrationHeaders (0.62.2):
139
+    - Folly (= 2018.10.22.00)
140
+    - glog
141
+    - React-Core/Default
142
+    - React-cxxreact (= 0.62.2)
143
+    - React-jsi (= 0.62.2)
144
+    - React-jsiexecutor (= 0.62.2)
145
+    - Yoga
146
+  - React-Core/RCTWebSocket (0.62.2):
147
+    - Folly (= 2018.10.22.00)
148
+    - glog
149
+    - React-Core/Default (= 0.62.2)
150
+    - React-cxxreact (= 0.62.2)
151
+    - React-jsi (= 0.62.2)
152
+    - React-jsiexecutor (= 0.62.2)
153
+    - Yoga
154
+  - React-CoreModules (0.62.2):
155
+    - FBReactNativeSpec (= 0.62.2)
156
+    - Folly (= 2018.10.22.00)
157
+    - RCTTypeSafety (= 0.62.2)
158
+    - React-Core/CoreModulesHeaders (= 0.62.2)
159
+    - React-RCTImage (= 0.62.2)
160
+    - ReactCommon/turbomodule/core (= 0.62.2)
161
+  - React-cxxreact (0.62.2):
162
+    - boost-for-react-native (= 1.63.0)
163
+    - DoubleConversion
164
+    - Folly (= 2018.10.22.00)
165
+    - glog
166
+    - React-jsinspector (= 0.62.2)
167
+  - React-jsi (0.62.2):
168
+    - boost-for-react-native (= 1.63.0)
169
+    - DoubleConversion
170
+    - Folly (= 2018.10.22.00)
171
+    - glog
172
+    - React-jsi/Default (= 0.62.2)
173
+  - React-jsi/Default (0.62.2):
174
+    - boost-for-react-native (= 1.63.0)
175
+    - DoubleConversion
176
+    - Folly (= 2018.10.22.00)
177
+    - glog
178
+  - React-jsiexecutor (0.62.2):
179
+    - DoubleConversion
180
+    - Folly (= 2018.10.22.00)
181
+    - glog
182
+    - React-cxxreact (= 0.62.2)
183
+    - React-jsi (= 0.62.2)
184
+  - React-jsinspector (0.62.2)
185
+  - react-native-webview (9.4.0):
186
+    - React
187
+  - React-RCTActionSheet (0.62.2):
188
+    - React-Core/RCTActionSheetHeaders (= 0.62.2)
189
+  - React-RCTAnimation (0.62.2):
190
+    - FBReactNativeSpec (= 0.62.2)
191
+    - Folly (= 2018.10.22.00)
192
+    - RCTTypeSafety (= 0.62.2)
193
+    - React-Core/RCTAnimationHeaders (= 0.62.2)
194
+    - ReactCommon/turbomodule/core (= 0.62.2)
195
+  - React-RCTBlob (0.62.2):
196
+    - FBReactNativeSpec (= 0.62.2)
197
+    - Folly (= 2018.10.22.00)
198
+    - React-Core/RCTBlobHeaders (= 0.62.2)
199
+    - React-Core/RCTWebSocket (= 0.62.2)
200
+    - React-jsi (= 0.62.2)
201
+    - React-RCTNetwork (= 0.62.2)
202
+    - ReactCommon/turbomodule/core (= 0.62.2)
203
+  - React-RCTImage (0.62.2):
204
+    - FBReactNativeSpec (= 0.62.2)
205
+    - Folly (= 2018.10.22.00)
206
+    - RCTTypeSafety (= 0.62.2)
207
+    - React-Core/RCTImageHeaders (= 0.62.2)
208
+    - React-RCTNetwork (= 0.62.2)
209
+    - ReactCommon/turbomodule/core (= 0.62.2)
210
+  - React-RCTLinking (0.62.2):
211
+    - FBReactNativeSpec (= 0.62.2)
212
+    - React-Core/RCTLinkingHeaders (= 0.62.2)
213
+    - ReactCommon/turbomodule/core (= 0.62.2)
214
+  - React-RCTNetwork (0.62.2):
215
+    - FBReactNativeSpec (= 0.62.2)
216
+    - Folly (= 2018.10.22.00)
217
+    - RCTTypeSafety (= 0.62.2)
218
+    - React-Core/RCTNetworkHeaders (= 0.62.2)
219
+    - ReactCommon/turbomodule/core (= 0.62.2)
220
+  - React-RCTSettings (0.62.2):
221
+    - FBReactNativeSpec (= 0.62.2)
222
+    - Folly (= 2018.10.22.00)
223
+    - RCTTypeSafety (= 0.62.2)
224
+    - React-Core/RCTSettingsHeaders (= 0.62.2)
225
+    - ReactCommon/turbomodule/core (= 0.62.2)
226
+  - React-RCTText (0.62.2):
227
+    - React-Core/RCTTextHeaders (= 0.62.2)
228
+  - React-RCTVibration (0.62.2):
229
+    - FBReactNativeSpec (= 0.62.2)
230
+    - Folly (= 2018.10.22.00)
231
+    - React-Core/RCTVibrationHeaders (= 0.62.2)
232
+    - ReactCommon/turbomodule/core (= 0.62.2)
233
+  - ReactCommon/callinvoker (0.62.2):
234
+    - DoubleConversion
235
+    - Folly (= 2018.10.22.00)
236
+    - glog
237
+    - React-cxxreact (= 0.62.2)
238
+  - ReactCommon/turbomodule/core (0.62.2):
239
+    - DoubleConversion
240
+    - Folly (= 2018.10.22.00)
241
+    - glog
242
+    - React-Core (= 0.62.2)
243
+    - React-cxxreact (= 0.62.2)
244
+    - React-jsi (= 0.62.2)
245
+    - ReactCommon/callinvoker (= 0.62.2)
246
+  - Yoga (1.14.0)
247
+
248
+DEPENDENCIES:
249
+  - DoubleConversion (from `../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
250
+  - FBLazyVector (from `../../node_modules/react-native/Libraries/FBLazyVector`)
251
+  - FBReactNativeSpec (from `../../node_modules/react-native/Libraries/FBReactNativeSpec`)
252
+  - Folly (from `../../node_modules/react-native/third-party-podspecs/Folly.podspec`)
253
+  - glog (from `../../node_modules/react-native/third-party-podspecs/glog.podspec`)
254
+  - RCTRequired (from `../../node_modules/react-native/Libraries/RCTRequired`)
255
+  - RCTTypeSafety (from `../../node_modules/react-native/Libraries/TypeSafety`)
256
+  - React (from `../../node_modules/react-native/`)
257
+  - React-Core (from `../../node_modules/react-native/`)
258
+  - React-Core/DevSupport (from `../../node_modules/react-native/`)
259
+  - React-Core/RCTWebSocket (from `../../node_modules/react-native/`)
260
+  - React-CoreModules (from `../../node_modules/react-native/React/CoreModules`)
261
+  - React-cxxreact (from `../../node_modules/react-native/ReactCommon/cxxreact`)
262
+  - React-jsi (from `../../node_modules/react-native/ReactCommon/jsi`)
263
+  - React-jsiexecutor (from `../../node_modules/react-native/ReactCommon/jsiexecutor`)
264
+  - React-jsinspector (from `../../node_modules/react-native/ReactCommon/jsinspector`)
265
+  - react-native-webview (from `../..`)
266
+  - React-RCTActionSheet (from `../../node_modules/react-native/Libraries/ActionSheetIOS`)
267
+  - React-RCTAnimation (from `../../node_modules/react-native/Libraries/NativeAnimation`)
268
+  - React-RCTBlob (from `../../node_modules/react-native/Libraries/Blob`)
269
+  - React-RCTImage (from `../../node_modules/react-native/Libraries/Image`)
270
+  - React-RCTLinking (from `../../node_modules/react-native/Libraries/LinkingIOS`)
271
+  - React-RCTNetwork (from `../../node_modules/react-native/Libraries/Network`)
272
+  - React-RCTSettings (from `../../node_modules/react-native/Libraries/Settings`)
273
+  - React-RCTText (from `../../node_modules/react-native/Libraries/Text`)
274
+  - React-RCTVibration (from `../../node_modules/react-native/Libraries/Vibration`)
275
+  - ReactCommon/callinvoker (from `../../node_modules/react-native/ReactCommon`)
276
+  - ReactCommon/turbomodule/core (from `../../node_modules/react-native/ReactCommon`)
277
+  - Yoga (from `../../node_modules/react-native/ReactCommon/yoga`)
278
+
279
+SPEC REPOS:
280
+  trunk:
281
+    - boost-for-react-native
282
+
283
+EXTERNAL SOURCES:
284
+  DoubleConversion:
285
+    :podspec: "../../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
286
+  FBLazyVector:
287
+    :path: "../../node_modules/react-native/Libraries/FBLazyVector"
288
+  FBReactNativeSpec:
289
+    :path: "../../node_modules/react-native/Libraries/FBReactNativeSpec"
290
+  Folly:
291
+    :podspec: "../../node_modules/react-native/third-party-podspecs/Folly.podspec"
292
+  glog:
293
+    :podspec: "../../node_modules/react-native/third-party-podspecs/glog.podspec"
294
+  RCTRequired:
295
+    :path: "../../node_modules/react-native/Libraries/RCTRequired"
296
+  RCTTypeSafety:
297
+    :path: "../../node_modules/react-native/Libraries/TypeSafety"
298
+  React:
299
+    :path: "../../node_modules/react-native/"
300
+  React-Core:
301
+    :path: "../../node_modules/react-native/"
302
+  React-CoreModules:
303
+    :path: "../../node_modules/react-native/React/CoreModules"
304
+  React-cxxreact:
305
+    :path: "../../node_modules/react-native/ReactCommon/cxxreact"
306
+  React-jsi:
307
+    :path: "../../node_modules/react-native/ReactCommon/jsi"
308
+  React-jsiexecutor:
309
+    :path: "../../node_modules/react-native/ReactCommon/jsiexecutor"
310
+  React-jsinspector:
311
+    :path: "../../node_modules/react-native/ReactCommon/jsinspector"
312
+  react-native-webview:
313
+    :path: "../.."
314
+  React-RCTActionSheet:
315
+    :path: "../../node_modules/react-native/Libraries/ActionSheetIOS"
316
+  React-RCTAnimation:
317
+    :path: "../../node_modules/react-native/Libraries/NativeAnimation"
318
+  React-RCTBlob:
319
+    :path: "../../node_modules/react-native/Libraries/Blob"
320
+  React-RCTImage:
321
+    :path: "../../node_modules/react-native/Libraries/Image"
322
+  React-RCTLinking:
323
+    :path: "../../node_modules/react-native/Libraries/LinkingIOS"
324
+  React-RCTNetwork:
325
+    :path: "../../node_modules/react-native/Libraries/Network"
326
+  React-RCTSettings:
327
+    :path: "../../node_modules/react-native/Libraries/Settings"
328
+  React-RCTText:
329
+    :path: "../../node_modules/react-native/Libraries/Text"
330
+  React-RCTVibration:
331
+    :path: "../../node_modules/react-native/Libraries/Vibration"
332
+  ReactCommon:
333
+    :path: "../../node_modules/react-native/ReactCommon"
334
+  Yoga:
335
+    :path: "../../node_modules/react-native/ReactCommon/yoga"
336
+
337
+SPEC CHECKSUMS:
338
+  boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
339
+  DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
340
+  FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
341
+  FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
342
+  Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
343
+  glog: 1f3da668190260b06b429bb211bfbee5cd790c28
344
+  RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
345
+  RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
346
+  React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
347
+  React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05
348
+  React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0
349
+  React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103
350
+  React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
351
+  React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
352
+  React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
353
+  react-native-webview: cf5527893252b3b036eea024a1da6996f7344c74
354
+  React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
355
+  React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
356
+  React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
357
+  React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3
358
+  React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2
359
+  React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44
360
+  React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a
361
+  React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
362
+  React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
363
+  ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
364
+  Yoga: 3ebccbdd559724312790e7742142d062476b698e
365
+
366
+PODFILE CHECKSUM: 767ae042fafc1aba97b301c58c340ff6f992aa27
367
+
368
+COCOAPODS: 1.9.1

+ 53
- 0
example/ios/example-tvOS/Info.plist 查看文件

@@ -0,0 +1,53 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleExecutable</key>
8
+	<string>$(EXECUTABLE_NAME)</string>
9
+	<key>CFBundleIdentifier</key>
10
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+	<key>CFBundleInfoDictionaryVersion</key>
12
+	<string>6.0</string>
13
+	<key>CFBundleName</key>
14
+	<string>$(PRODUCT_NAME)</string>
15
+	<key>CFBundlePackageType</key>
16
+	<string>APPL</string>
17
+	<key>CFBundleShortVersionString</key>
18
+	<string>1.0</string>
19
+	<key>CFBundleSignature</key>
20
+	<string>????</string>
21
+	<key>CFBundleVersion</key>
22
+	<string>1</string>
23
+	<key>LSRequiresIPhoneOS</key>
24
+	<true/>
25
+	<key>NSAppTransportSecurity</key>
26
+	<dict>
27
+		<key>NSExceptionDomains</key>
28
+		<dict>
29
+			<key>localhost</key>
30
+			<dict>
31
+				<key>NSExceptionAllowsInsecureHTTPLoads</key>
32
+				<true/>
33
+			</dict>
34
+		</dict>
35
+	</dict>
36
+	<key>NSLocationWhenInUseUsageDescription</key>
37
+	<string></string>
38
+	<key>UILaunchStoryboardName</key>
39
+	<string>LaunchScreen</string>
40
+	<key>UIRequiredDeviceCapabilities</key>
41
+	<array>
42
+		<string>armv7</string>
43
+	</array>
44
+	<key>UISupportedInterfaceOrientations</key>
45
+	<array>
46
+		<string>UIInterfaceOrientationPortrait</string>
47
+		<string>UIInterfaceOrientationLandscapeLeft</string>
48
+		<string>UIInterfaceOrientationLandscapeRight</string>
49
+	</array>
50
+	<key>UIViewControllerBasedStatusBarAppearance</key>
51
+	<false/>
52
+</dict>
53
+</plist>

+ 24
- 0
example/ios/example-tvOSTests/Info.plist 查看文件

@@ -0,0 +1,24 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleExecutable</key>
8
+	<string>$(EXECUTABLE_NAME)</string>
9
+	<key>CFBundleIdentifier</key>
10
+	<string>org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)</string>
11
+	<key>CFBundleInfoDictionaryVersion</key>
12
+	<string>6.0</string>
13
+	<key>CFBundleName</key>
14
+	<string>$(PRODUCT_NAME)</string>
15
+	<key>CFBundlePackageType</key>
16
+	<string>BNDL</string>
17
+	<key>CFBundleShortVersionString</key>
18
+	<string>1.0</string>
19
+	<key>CFBundleSignature</key>
20
+	<string>????</string>
21
+	<key>CFBundleVersion</key>
22
+	<string>1</string>
23
+</dict>
24
+</plist>

+ 929
- 0
example/ios/example.xcodeproj/project.pbxproj 查看文件

@@ -0,0 +1,929 @@
1
+// !$*UTF8*$!
2
+{
3
+	archiveVersion = 1;
4
+	classes = {
5
+	};
6
+	objectVersion = 46;
7
+	objects = {
8
+
9
+/* Begin PBXBuildFile section */
10
+		00E356F31AD99517003FC87E /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; };
11
+		13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
12
+		13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13
+		13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
14
+		13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
15
+		2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
16
+		2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
17
+		2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
18
+		2DCD954D1E0B4F2C00145EB5 /* exampleTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* exampleTests.m */; };
19
+		646BD8E8CDDF5A464B5419B3 /* libPods-example-tvOS-example-tvOSTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4830F7139A954350DD22DE4A /* libPods-example-tvOS-example-tvOSTests.a */; };
20
+		C7D826CF866C25BE421302B6 /* libPods-example-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F9A8A9F158876EC099CFA57A /* libPods-example-tvOS.a */; };
21
+		D0E3313DFCE78BFCB650F812 /* libPods-exampleTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB301596D47BBAD9E9C0A45A /* libPods-exampleTests.a */; };
22
+		E719A6E171791CD8906B3D55 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 917A19FC1EBE6E8B85FE404D /* libPods-example.a */; };
23
+/* End PBXBuildFile section */
24
+
25
+/* Begin PBXContainerItemProxy section */
26
+		00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
27
+			isa = PBXContainerItemProxy;
28
+			containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
29
+			proxyType = 1;
30
+			remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
31
+			remoteInfo = example;
32
+		};
33
+		2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = {
34
+			isa = PBXContainerItemProxy;
35
+			containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
36
+			proxyType = 1;
37
+			remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7;
38
+			remoteInfo = "example-tvOS";
39
+		};
40
+/* End PBXContainerItemProxy section */
41
+
42
+/* Begin PBXFileReference section */
43
+		008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = "<group>"; };
44
+		00E356EE1AD99517003FC87E /* exampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = exampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
45
+		00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
46
+		00E356F21AD99517003FC87E /* exampleTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = exampleTests.m; sourceTree = "<group>"; };
47
+		13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
48
+		13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = example/AppDelegate.h; sourceTree = "<group>"; };
49
+		13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = example/AppDelegate.m; sourceTree = "<group>"; };
50
+		13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
51
+		13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; };
52
+		13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; };
53
+		13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = example/main.m; sourceTree = "<group>"; };
54
+		2D02E47B1E0B4A5D006451C7 /* example-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "example-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
55
+		2D02E4901E0B4A5D006451C7 /* example-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "example-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
56
+		41B6A4553C4F552488B69B01 /* Pods-exampleTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-exampleTests.release.xcconfig"; path = "Target Support Files/Pods-exampleTests/Pods-exampleTests.release.xcconfig"; sourceTree = "<group>"; };
57
+		4372A2FD2D749DE5C9FD8D3E /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; };
58
+		4830F7139A954350DD22DE4A /* libPods-example-tvOS-example-tvOSTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-tvOS-example-tvOSTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
59
+		4FDD34C422D711AC8A7B10A7 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; };
60
+		59A4F27CAD1B7EFE80917453 /* Pods-example-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-example-tvOSTests/Pods-example-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
61
+		6517B8E7187010A1D58A96EE /* Pods-example-tvOS-example-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOS-example-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-example-tvOS-example-tvOSTests/Pods-example-tvOS-example-tvOSTests.debug.xcconfig"; sourceTree = "<group>"; };
62
+		775F6B7492793F5DB7ECE95B /* Pods-example-tvOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOS.debug.xcconfig"; path = "Target Support Files/Pods-example-tvOS/Pods-example-tvOS.debug.xcconfig"; sourceTree = "<group>"; };
63
+		8A20011E75A0AD2EC5C6EAE9 /* Pods-exampleTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-exampleTests.debug.xcconfig"; path = "Target Support Files/Pods-exampleTests/Pods-exampleTests.debug.xcconfig"; sourceTree = "<group>"; };
64
+		917A19FC1EBE6E8B85FE404D /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
65
+		CB301596D47BBAD9E9C0A45A /* libPods-exampleTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-exampleTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
66
+		ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
67
+		ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
68
+		EDF80BE96CF92848F4E926EA /* Pods-example-tvOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOS.release.xcconfig"; path = "Target Support Files/Pods-example-tvOS/Pods-example-tvOS.release.xcconfig"; sourceTree = "<group>"; };
69
+		EE3925F4209E17ECF3E692D1 /* Pods-example-tvOS-example-tvOSTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOS-example-tvOSTests.release.xcconfig"; path = "Target Support Files/Pods-example-tvOS-example-tvOSTests/Pods-example-tvOS-example-tvOSTests.release.xcconfig"; sourceTree = "<group>"; };
70
+		F67BC8D73DE103BA10A5488D /* Pods-example-tvOSTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example-tvOSTests.debug.xcconfig"; path = "Target Support Files/Pods-example-tvOSTests/Pods-example-tvOSTests.debug.xcconfig"; sourceTree = "<group>"; };
71
+		F9A8A9F158876EC099CFA57A /* libPods-example-tvOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example-tvOS.a"; sourceTree = BUILT_PRODUCTS_DIR; };
72
+/* End PBXFileReference section */
73
+
74
+/* Begin PBXFrameworksBuildPhase section */
75
+		00E356EB1AD99517003FC87E /* Frameworks */ = {
76
+			isa = PBXFrameworksBuildPhase;
77
+			buildActionMask = 2147483647;
78
+			files = (
79
+				D0E3313DFCE78BFCB650F812 /* libPods-exampleTests.a in Frameworks */,
80
+			);
81
+			runOnlyForDeploymentPostprocessing = 0;
82
+		};
83
+		13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
84
+			isa = PBXFrameworksBuildPhase;
85
+			buildActionMask = 2147483647;
86
+			files = (
87
+				E719A6E171791CD8906B3D55 /* libPods-example.a in Frameworks */,
88
+			);
89
+			runOnlyForDeploymentPostprocessing = 0;
90
+		};
91
+		2D02E4781E0B4A5D006451C7 /* Frameworks */ = {
92
+			isa = PBXFrameworksBuildPhase;
93
+			buildActionMask = 2147483647;
94
+			files = (
95
+				C7D826CF866C25BE421302B6 /* libPods-example-tvOS.a in Frameworks */,
96
+			);
97
+			runOnlyForDeploymentPostprocessing = 0;
98
+		};
99
+		2D02E48D1E0B4A5D006451C7 /* Frameworks */ = {
100
+			isa = PBXFrameworksBuildPhase;
101
+			buildActionMask = 2147483647;
102
+			files = (
103
+				646BD8E8CDDF5A464B5419B3 /* libPods-example-tvOS-example-tvOSTests.a in Frameworks */,
104
+			);
105
+			runOnlyForDeploymentPostprocessing = 0;
106
+		};
107
+/* End PBXFrameworksBuildPhase section */
108
+
109
+/* Begin PBXGroup section */
110
+		00E356EF1AD99517003FC87E /* exampleTests */ = {
111
+			isa = PBXGroup;
112
+			children = (
113
+				00E356F21AD99517003FC87E /* exampleTests.m */,
114
+				00E356F01AD99517003FC87E /* Supporting Files */,
115
+			);
116
+			path = exampleTests;
117
+			sourceTree = "<group>";
118
+		};
119
+		00E356F01AD99517003FC87E /* Supporting Files */ = {
120
+			isa = PBXGroup;
121
+			children = (
122
+				00E356F11AD99517003FC87E /* Info.plist */,
123
+			);
124
+			name = "Supporting Files";
125
+			sourceTree = "<group>";
126
+		};
127
+		13B07FAE1A68108700A75B9A /* example */ = {
128
+			isa = PBXGroup;
129
+			children = (
130
+				008F07F21AC5B25A0029DE68 /* main.jsbundle */,
131
+				13B07FAF1A68108700A75B9A /* AppDelegate.h */,
132
+				13B07FB01A68108700A75B9A /* AppDelegate.m */,
133
+				13B07FB51A68108700A75B9A /* Images.xcassets */,
134
+				13B07FB61A68108700A75B9A /* Info.plist */,
135
+				13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
136
+				13B07FB71A68108700A75B9A /* main.m */,
137
+			);
138
+			name = example;
139
+			sourceTree = "<group>";
140
+		};
141
+		2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
142
+			isa = PBXGroup;
143
+			children = (
144
+				ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
145
+				ED2971642150620600B7C4FE /* JavaScriptCore.framework */,
146
+				917A19FC1EBE6E8B85FE404D /* libPods-example.a */,
147
+				F9A8A9F158876EC099CFA57A /* libPods-example-tvOS.a */,
148
+				CB301596D47BBAD9E9C0A45A /* libPods-exampleTests.a */,
149
+				4830F7139A954350DD22DE4A /* libPods-example-tvOS-example-tvOSTests.a */,
150
+			);
151
+			name = Frameworks;
152
+			sourceTree = "<group>";
153
+		};
154
+		832341AE1AAA6A7D00B99B32 /* Libraries */ = {
155
+			isa = PBXGroup;
156
+			children = (
157
+			);
158
+			name = Libraries;
159
+			sourceTree = "<group>";
160
+		};
161
+		83CBB9F61A601CBA00E9B192 = {
162
+			isa = PBXGroup;
163
+			children = (
164
+				13B07FAE1A68108700A75B9A /* example */,
165
+				832341AE1AAA6A7D00B99B32 /* Libraries */,
166
+				00E356EF1AD99517003FC87E /* exampleTests */,
167
+				83CBBA001A601CBA00E9B192 /* Products */,
168
+				2D16E6871FA4F8E400B85C8A /* Frameworks */,
169
+				CCBCEDC2885B5181A2E42CE7 /* Pods */,
170
+			);
171
+			indentWidth = 2;
172
+			sourceTree = "<group>";
173
+			tabWidth = 2;
174
+			usesTabs = 0;
175
+		};
176
+		83CBBA001A601CBA00E9B192 /* Products */ = {
177
+			isa = PBXGroup;
178
+			children = (
179
+				13B07F961A680F5B00A75B9A /* example.app */,
180
+				00E356EE1AD99517003FC87E /* exampleTests.xctest */,
181
+				2D02E47B1E0B4A5D006451C7 /* example-tvOS.app */,
182
+				2D02E4901E0B4A5D006451C7 /* example-tvOSTests.xctest */,
183
+			);
184
+			name = Products;
185
+			sourceTree = "<group>";
186
+		};
187
+		CCBCEDC2885B5181A2E42CE7 /* Pods */ = {
188
+			isa = PBXGroup;
189
+			children = (
190
+				4372A2FD2D749DE5C9FD8D3E /* Pods-example.debug.xcconfig */,
191
+				4FDD34C422D711AC8A7B10A7 /* Pods-example.release.xcconfig */,
192
+				775F6B7492793F5DB7ECE95B /* Pods-example-tvOS.debug.xcconfig */,
193
+				EDF80BE96CF92848F4E926EA /* Pods-example-tvOS.release.xcconfig */,
194
+				F67BC8D73DE103BA10A5488D /* Pods-example-tvOSTests.debug.xcconfig */,
195
+				59A4F27CAD1B7EFE80917453 /* Pods-example-tvOSTests.release.xcconfig */,
196
+				8A20011E75A0AD2EC5C6EAE9 /* Pods-exampleTests.debug.xcconfig */,
197
+				41B6A4553C4F552488B69B01 /* Pods-exampleTests.release.xcconfig */,
198
+				6517B8E7187010A1D58A96EE /* Pods-example-tvOS-example-tvOSTests.debug.xcconfig */,
199
+				EE3925F4209E17ECF3E692D1 /* Pods-example-tvOS-example-tvOSTests.release.xcconfig */,
200
+			);
201
+			path = Pods;
202
+			sourceTree = "<group>";
203
+		};
204
+/* End PBXGroup section */
205
+
206
+/* Begin PBXNativeTarget section */
207
+		00E356ED1AD99517003FC87E /* exampleTests */ = {
208
+			isa = PBXNativeTarget;
209
+			buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */;
210
+			buildPhases = (
211
+				A2264F1CFC0047F3C8C5D43D /* [CP] Check Pods Manifest.lock */,
212
+				00E356EA1AD99517003FC87E /* Sources */,
213
+				00E356EB1AD99517003FC87E /* Frameworks */,
214
+				00E356EC1AD99517003FC87E /* Resources */,
215
+			);
216
+			buildRules = (
217
+			);
218
+			dependencies = (
219
+				00E356F51AD99517003FC87E /* PBXTargetDependency */,
220
+			);
221
+			name = exampleTests;
222
+			productName = exampleTests;
223
+			productReference = 00E356EE1AD99517003FC87E /* exampleTests.xctest */;
224
+			productType = "com.apple.product-type.bundle.unit-test";
225
+		};
226
+		13B07F861A680F5B00A75B9A /* example */ = {
227
+			isa = PBXNativeTarget;
228
+			buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */;
229
+			buildPhases = (
230
+				6D39E19069EEC1F18C65E89E /* [CP] Check Pods Manifest.lock */,
231
+				FD10A7F022414F080027D42C /* Start Packager */,
232
+				13B07F871A680F5B00A75B9A /* Sources */,
233
+				13B07F8C1A680F5B00A75B9A /* Frameworks */,
234
+				13B07F8E1A680F5B00A75B9A /* Resources */,
235
+				00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
236
+			);
237
+			buildRules = (
238
+			);
239
+			dependencies = (
240
+			);
241
+			name = example;
242
+			productName = example;
243
+			productReference = 13B07F961A680F5B00A75B9A /* example.app */;
244
+			productType = "com.apple.product-type.application";
245
+		};
246
+		2D02E47A1E0B4A5D006451C7 /* example-tvOS */ = {
247
+			isa = PBXNativeTarget;
248
+			buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "example-tvOS" */;
249
+			buildPhases = (
250
+				B980633124DB020AB7EC55E3 /* [CP] Check Pods Manifest.lock */,
251
+				FD10A7F122414F3F0027D42C /* Start Packager */,
252
+				2D02E4771E0B4A5D006451C7 /* Sources */,
253
+				2D02E4781E0B4A5D006451C7 /* Frameworks */,
254
+				2D02E4791E0B4A5D006451C7 /* Resources */,
255
+				2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */,
256
+			);
257
+			buildRules = (
258
+			);
259
+			dependencies = (
260
+			);
261
+			name = "example-tvOS";
262
+			productName = "example-tvOS";
263
+			productReference = 2D02E47B1E0B4A5D006451C7 /* example-tvOS.app */;
264
+			productType = "com.apple.product-type.application";
265
+		};
266
+		2D02E48F1E0B4A5D006451C7 /* example-tvOSTests */ = {
267
+			isa = PBXNativeTarget;
268
+			buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "example-tvOSTests" */;
269
+			buildPhases = (
270
+				56202D3091BCA37A2C594528 /* [CP] Check Pods Manifest.lock */,
271
+				2D02E48C1E0B4A5D006451C7 /* Sources */,
272
+				2D02E48D1E0B4A5D006451C7 /* Frameworks */,
273
+				2D02E48E1E0B4A5D006451C7 /* Resources */,
274
+			);
275
+			buildRules = (
276
+			);
277
+			dependencies = (
278
+				2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */,
279
+			);
280
+			name = "example-tvOSTests";
281
+			productName = "example-tvOSTests";
282
+			productReference = 2D02E4901E0B4A5D006451C7 /* example-tvOSTests.xctest */;
283
+			productType = "com.apple.product-type.bundle.unit-test";
284
+		};
285
+/* End PBXNativeTarget section */
286
+
287
+/* Begin PBXProject section */
288
+		83CBB9F71A601CBA00E9B192 /* Project object */ = {
289
+			isa = PBXProject;
290
+			attributes = {
291
+				LastUpgradeCheck = 0940;
292
+				ORGANIZATIONNAME = Facebook;
293
+				TargetAttributes = {
294
+					00E356ED1AD99517003FC87E = {
295
+						CreatedOnToolsVersion = 6.2;
296
+						TestTargetID = 13B07F861A680F5B00A75B9A;
297
+					};
298
+					2D02E47A1E0B4A5D006451C7 = {
299
+						CreatedOnToolsVersion = 8.2.1;
300
+						ProvisioningStyle = Automatic;
301
+					};
302
+					2D02E48F1E0B4A5D006451C7 = {
303
+						CreatedOnToolsVersion = 8.2.1;
304
+						ProvisioningStyle = Automatic;
305
+						TestTargetID = 2D02E47A1E0B4A5D006451C7;
306
+					};
307
+				};
308
+			};
309
+			buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */;
310
+			compatibilityVersion = "Xcode 3.2";
311
+			developmentRegion = English;
312
+			hasScannedForEncodings = 0;
313
+			knownRegions = (
314
+				English,
315
+				en,
316
+				Base,
317
+			);
318
+			mainGroup = 83CBB9F61A601CBA00E9B192;
319
+			productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
320
+			projectDirPath = "";
321
+			projectRoot = "";
322
+			targets = (
323
+				13B07F861A680F5B00A75B9A /* example */,
324
+				00E356ED1AD99517003FC87E /* exampleTests */,
325
+				2D02E47A1E0B4A5D006451C7 /* example-tvOS */,
326
+				2D02E48F1E0B4A5D006451C7 /* example-tvOSTests */,
327
+			);
328
+		};
329
+/* End PBXProject section */
330
+
331
+/* Begin PBXResourcesBuildPhase section */
332
+		00E356EC1AD99517003FC87E /* Resources */ = {
333
+			isa = PBXResourcesBuildPhase;
334
+			buildActionMask = 2147483647;
335
+			files = (
336
+			);
337
+			runOnlyForDeploymentPostprocessing = 0;
338
+		};
339
+		13B07F8E1A680F5B00A75B9A /* Resources */ = {
340
+			isa = PBXResourcesBuildPhase;
341
+			buildActionMask = 2147483647;
342
+			files = (
343
+				13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
344
+				13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */,
345
+			);
346
+			runOnlyForDeploymentPostprocessing = 0;
347
+		};
348
+		2D02E4791E0B4A5D006451C7 /* Resources */ = {
349
+			isa = PBXResourcesBuildPhase;
350
+			buildActionMask = 2147483647;
351
+			files = (
352
+				2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */,
353
+			);
354
+			runOnlyForDeploymentPostprocessing = 0;
355
+		};
356
+		2D02E48E1E0B4A5D006451C7 /* Resources */ = {
357
+			isa = PBXResourcesBuildPhase;
358
+			buildActionMask = 2147483647;
359
+			files = (
360
+			);
361
+			runOnlyForDeploymentPostprocessing = 0;
362
+		};
363
+/* End PBXResourcesBuildPhase section */
364
+
365
+/* Begin PBXShellScriptBuildPhase section */
366
+		00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
367
+			isa = PBXShellScriptBuildPhase;
368
+			buildActionMask = 2147483647;
369
+			files = (
370
+			);
371
+			inputPaths = (
372
+			);
373
+			name = "Bundle React Native code and images";
374
+			outputPaths = (
375
+			);
376
+			runOnlyForDeploymentPostprocessing = 0;
377
+			shellPath = /bin/sh;
378
+			shellScript = "export NODE_BINARY=node\n../../node_modules/react-native/scripts/react-native-xcode.sh\n";
379
+		};
380
+		2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
381
+			isa = PBXShellScriptBuildPhase;
382
+			buildActionMask = 2147483647;
383
+			files = (
384
+			);
385
+			inputPaths = (
386
+			);
387
+			name = "Bundle React Native Code And Images";
388
+			outputPaths = (
389
+			);
390
+			runOnlyForDeploymentPostprocessing = 0;
391
+			shellPath = /bin/sh;
392
+			shellScript = "export NODE_BINARY=node\n../../node_modules/react-native/scripts/react-native-xcode.sh\n";
393
+		};
394
+		56202D3091BCA37A2C594528 /* [CP] Check Pods Manifest.lock */ = {
395
+			isa = PBXShellScriptBuildPhase;
396
+			buildActionMask = 2147483647;
397
+			files = (
398
+			);
399
+			inputFileListPaths = (
400
+			);
401
+			inputPaths = (
402
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
403
+				"${PODS_ROOT}/Manifest.lock",
404
+			);
405
+			name = "[CP] Check Pods Manifest.lock";
406
+			outputFileListPaths = (
407
+			);
408
+			outputPaths = (
409
+				"$(DERIVED_FILE_DIR)/Pods-example-tvOS-example-tvOSTests-checkManifestLockResult.txt",
410
+			);
411
+			runOnlyForDeploymentPostprocessing = 0;
412
+			shellPath = /bin/sh;
413
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
414
+			showEnvVarsInLog = 0;
415
+		};
416
+		6D39E19069EEC1F18C65E89E /* [CP] Check Pods Manifest.lock */ = {
417
+			isa = PBXShellScriptBuildPhase;
418
+			buildActionMask = 2147483647;
419
+			files = (
420
+			);
421
+			inputFileListPaths = (
422
+			);
423
+			inputPaths = (
424
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
425
+				"${PODS_ROOT}/Manifest.lock",
426
+			);
427
+			name = "[CP] Check Pods Manifest.lock";
428
+			outputFileListPaths = (
429
+			);
430
+			outputPaths = (
431
+				"$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt",
432
+			);
433
+			runOnlyForDeploymentPostprocessing = 0;
434
+			shellPath = /bin/sh;
435
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
436
+			showEnvVarsInLog = 0;
437
+		};
438
+		A2264F1CFC0047F3C8C5D43D /* [CP] Check Pods Manifest.lock */ = {
439
+			isa = PBXShellScriptBuildPhase;
440
+			buildActionMask = 2147483647;
441
+			files = (
442
+			);
443
+			inputFileListPaths = (
444
+			);
445
+			inputPaths = (
446
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
447
+				"${PODS_ROOT}/Manifest.lock",
448
+			);
449
+			name = "[CP] Check Pods Manifest.lock";
450
+			outputFileListPaths = (
451
+			);
452
+			outputPaths = (
453
+				"$(DERIVED_FILE_DIR)/Pods-exampleTests-checkManifestLockResult.txt",
454
+			);
455
+			runOnlyForDeploymentPostprocessing = 0;
456
+			shellPath = /bin/sh;
457
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
458
+			showEnvVarsInLog = 0;
459
+		};
460
+		B980633124DB020AB7EC55E3 /* [CP] Check Pods Manifest.lock */ = {
461
+			isa = PBXShellScriptBuildPhase;
462
+			buildActionMask = 2147483647;
463
+			files = (
464
+			);
465
+			inputFileListPaths = (
466
+			);
467
+			inputPaths = (
468
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
469
+				"${PODS_ROOT}/Manifest.lock",
470
+			);
471
+			name = "[CP] Check Pods Manifest.lock";
472
+			outputFileListPaths = (
473
+			);
474
+			outputPaths = (
475
+				"$(DERIVED_FILE_DIR)/Pods-example-tvOS-checkManifestLockResult.txt",
476
+			);
477
+			runOnlyForDeploymentPostprocessing = 0;
478
+			shellPath = /bin/sh;
479
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
480
+			showEnvVarsInLog = 0;
481
+		};
482
+		FD10A7F022414F080027D42C /* Start Packager */ = {
483
+			isa = PBXShellScriptBuildPhase;
484
+			buildActionMask = 2147483647;
485
+			files = (
486
+			);
487
+			inputFileListPaths = (
488
+			);
489
+			inputPaths = (
490
+			);
491
+			name = "Start Packager";
492
+			outputFileListPaths = (
493
+			);
494
+			outputPaths = (
495
+			);
496
+			runOnlyForDeploymentPostprocessing = 0;
497
+			shellPath = /bin/sh;
498
+			shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n  if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n    if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n      echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n      exit 2\n    fi\n  else\n    open \"$SRCROOT/../../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n  fi\nfi\n";
499
+			showEnvVarsInLog = 0;
500
+		};
501
+		FD10A7F122414F3F0027D42C /* Start Packager */ = {
502
+			isa = PBXShellScriptBuildPhase;
503
+			buildActionMask = 2147483647;
504
+			files = (
505
+			);
506
+			inputFileListPaths = (
507
+			);
508
+			inputPaths = (
509
+			);
510
+			name = "Start Packager";
511
+			outputFileListPaths = (
512
+			);
513
+			outputPaths = (
514
+			);
515
+			runOnlyForDeploymentPostprocessing = 0;
516
+			shellPath = /bin/sh;
517
+			shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n  if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n    if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n      echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n      exit 2\n    fi\n  else\n    open \"$SRCROOT/../../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n  fi\nfi\n";
518
+			showEnvVarsInLog = 0;
519
+		};
520
+/* End PBXShellScriptBuildPhase section */
521
+
522
+/* Begin PBXSourcesBuildPhase section */
523
+		00E356EA1AD99517003FC87E /* Sources */ = {
524
+			isa = PBXSourcesBuildPhase;
525
+			buildActionMask = 2147483647;
526
+			files = (
527
+				00E356F31AD99517003FC87E /* exampleTests.m in Sources */,
528
+			);
529
+			runOnlyForDeploymentPostprocessing = 0;
530
+		};
531
+		13B07F871A680F5B00A75B9A /* Sources */ = {
532
+			isa = PBXSourcesBuildPhase;
533
+			buildActionMask = 2147483647;
534
+			files = (
535
+				13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */,
536
+				13B07FC11A68108700A75B9A /* main.m in Sources */,
537
+			);
538
+			runOnlyForDeploymentPostprocessing = 0;
539
+		};
540
+		2D02E4771E0B4A5D006451C7 /* Sources */ = {
541
+			isa = PBXSourcesBuildPhase;
542
+			buildActionMask = 2147483647;
543
+			files = (
544
+				2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */,
545
+				2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */,
546
+			);
547
+			runOnlyForDeploymentPostprocessing = 0;
548
+		};
549
+		2D02E48C1E0B4A5D006451C7 /* Sources */ = {
550
+			isa = PBXSourcesBuildPhase;
551
+			buildActionMask = 2147483647;
552
+			files = (
553
+				2DCD954D1E0B4F2C00145EB5 /* exampleTests.m in Sources */,
554
+			);
555
+			runOnlyForDeploymentPostprocessing = 0;
556
+		};
557
+/* End PBXSourcesBuildPhase section */
558
+
559
+/* Begin PBXTargetDependency section */
560
+		00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
561
+			isa = PBXTargetDependency;
562
+			target = 13B07F861A680F5B00A75B9A /* example */;
563
+			targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
564
+		};
565
+		2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = {
566
+			isa = PBXTargetDependency;
567
+			target = 2D02E47A1E0B4A5D006451C7 /* example-tvOS */;
568
+			targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */;
569
+		};
570
+/* End PBXTargetDependency section */
571
+
572
+/* Begin PBXVariantGroup section */
573
+		13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
574
+			isa = PBXVariantGroup;
575
+			children = (
576
+				13B07FB21A68108700A75B9A /* Base */,
577
+			);
578
+			name = LaunchScreen.xib;
579
+			path = example;
580
+			sourceTree = "<group>";
581
+		};
582
+/* End PBXVariantGroup section */
583
+
584
+/* Begin XCBuildConfiguration section */
585
+		00E356F61AD99517003FC87E /* Debug */ = {
586
+			isa = XCBuildConfiguration;
587
+			baseConfigurationReference = 8A20011E75A0AD2EC5C6EAE9 /* Pods-exampleTests.debug.xcconfig */;
588
+			buildSettings = {
589
+				BUNDLE_LOADER = "$(TEST_HOST)";
590
+				GCC_PREPROCESSOR_DEFINITIONS = (
591
+					"DEBUG=1",
592
+					"$(inherited)",
593
+				);
594
+				INFOPLIST_FILE = exampleTests/Info.plist;
595
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
596
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
597
+				OTHER_LDFLAGS = (
598
+					"-ObjC",
599
+					"-lc++",
600
+					"$(inherited)",
601
+				);
602
+				PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
603
+				PRODUCT_NAME = "$(TARGET_NAME)";
604
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example";
605
+			};
606
+			name = Debug;
607
+		};
608
+		00E356F71AD99517003FC87E /* Release */ = {
609
+			isa = XCBuildConfiguration;
610
+			baseConfigurationReference = 41B6A4553C4F552488B69B01 /* Pods-exampleTests.release.xcconfig */;
611
+			buildSettings = {
612
+				BUNDLE_LOADER = "$(TEST_HOST)";
613
+				COPY_PHASE_STRIP = NO;
614
+				INFOPLIST_FILE = exampleTests/Info.plist;
615
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
616
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
617
+				OTHER_LDFLAGS = (
618
+					"-ObjC",
619
+					"-lc++",
620
+					"$(inherited)",
621
+				);
622
+				PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
623
+				PRODUCT_NAME = "$(TARGET_NAME)";
624
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/example";
625
+			};
626
+			name = Release;
627
+		};
628
+		13B07F941A680F5B00A75B9A /* Debug */ = {
629
+			isa = XCBuildConfiguration;
630
+			baseConfigurationReference = 4372A2FD2D749DE5C9FD8D3E /* Pods-example.debug.xcconfig */;
631
+			buildSettings = {
632
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
633
+				CURRENT_PROJECT_VERSION = 1;
634
+				DEAD_CODE_STRIPPING = NO;
635
+				INFOPLIST_FILE = example/Info.plist;
636
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
637
+				OTHER_LDFLAGS = (
638
+					"$(inherited)",
639
+					"-ObjC",
640
+					"-lc++",
641
+				);
642
+				PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
643
+				PRODUCT_NAME = example;
644
+				VERSIONING_SYSTEM = "apple-generic";
645
+			};
646
+			name = Debug;
647
+		};
648
+		13B07F951A680F5B00A75B9A /* Release */ = {
649
+			isa = XCBuildConfiguration;
650
+			baseConfigurationReference = 4FDD34C422D711AC8A7B10A7 /* Pods-example.release.xcconfig */;
651
+			buildSettings = {
652
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
653
+				CURRENT_PROJECT_VERSION = 1;
654
+				INFOPLIST_FILE = example/Info.plist;
655
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
656
+				OTHER_LDFLAGS = (
657
+					"$(inherited)",
658
+					"-ObjC",
659
+					"-lc++",
660
+				);
661
+				PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
662
+				PRODUCT_NAME = example;
663
+				VERSIONING_SYSTEM = "apple-generic";
664
+			};
665
+			name = Release;
666
+		};
667
+		2D02E4971E0B4A5E006451C7 /* Debug */ = {
668
+			isa = XCBuildConfiguration;
669
+			baseConfigurationReference = 775F6B7492793F5DB7ECE95B /* Pods-example-tvOS.debug.xcconfig */;
670
+			buildSettings = {
671
+				ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
672
+				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
673
+				CLANG_ANALYZER_NONNULL = YES;
674
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
675
+				CLANG_WARN_INFINITE_RECURSION = YES;
676
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
677
+				DEBUG_INFORMATION_FORMAT = dwarf;
678
+				ENABLE_TESTABILITY = YES;
679
+				GCC_NO_COMMON_BLOCKS = YES;
680
+				INFOPLIST_FILE = "example-tvOS/Info.plist";
681
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
682
+				OTHER_LDFLAGS = (
683
+					"$(inherited)",
684
+					"-ObjC",
685
+					"-lc++",
686
+				);
687
+				PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.example-tvOS";
688
+				PRODUCT_NAME = "$(TARGET_NAME)";
689
+				SDKROOT = appletvos;
690
+				TARGETED_DEVICE_FAMILY = 3;
691
+				TVOS_DEPLOYMENT_TARGET = 9.2;
692
+			};
693
+			name = Debug;
694
+		};
695
+		2D02E4981E0B4A5E006451C7 /* Release */ = {
696
+			isa = XCBuildConfiguration;
697
+			baseConfigurationReference = EDF80BE96CF92848F4E926EA /* Pods-example-tvOS.release.xcconfig */;
698
+			buildSettings = {
699
+				ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
700
+				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
701
+				CLANG_ANALYZER_NONNULL = YES;
702
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
703
+				CLANG_WARN_INFINITE_RECURSION = YES;
704
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
705
+				COPY_PHASE_STRIP = NO;
706
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
707
+				GCC_NO_COMMON_BLOCKS = YES;
708
+				INFOPLIST_FILE = "example-tvOS/Info.plist";
709
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
710
+				OTHER_LDFLAGS = (
711
+					"$(inherited)",
712
+					"-ObjC",
713
+					"-lc++",
714
+				);
715
+				PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.example-tvOS";
716
+				PRODUCT_NAME = "$(TARGET_NAME)";
717
+				SDKROOT = appletvos;
718
+				TARGETED_DEVICE_FAMILY = 3;
719
+				TVOS_DEPLOYMENT_TARGET = 9.2;
720
+			};
721
+			name = Release;
722
+		};
723
+		2D02E4991E0B4A5E006451C7 /* Debug */ = {
724
+			isa = XCBuildConfiguration;
725
+			baseConfigurationReference = 6517B8E7187010A1D58A96EE /* Pods-example-tvOS-example-tvOSTests.debug.xcconfig */;
726
+			buildSettings = {
727
+				BUNDLE_LOADER = "$(TEST_HOST)";
728
+				CLANG_ANALYZER_NONNULL = YES;
729
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
730
+				CLANG_WARN_INFINITE_RECURSION = YES;
731
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
732
+				DEBUG_INFORMATION_FORMAT = dwarf;
733
+				ENABLE_TESTABILITY = YES;
734
+				GCC_NO_COMMON_BLOCKS = YES;
735
+				INFOPLIST_FILE = "example-tvOSTests/Info.plist";
736
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
737
+				OTHER_LDFLAGS = (
738
+					"$(inherited)",
739
+					"-ObjC",
740
+					"-lc++",
741
+				);
742
+				PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.example-tvOSTests";
743
+				PRODUCT_NAME = "$(TARGET_NAME)";
744
+				SDKROOT = appletvos;
745
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example-tvOS.app/example-tvOS";
746
+				TVOS_DEPLOYMENT_TARGET = 10.1;
747
+			};
748
+			name = Debug;
749
+		};
750
+		2D02E49A1E0B4A5E006451C7 /* Release */ = {
751
+			isa = XCBuildConfiguration;
752
+			baseConfigurationReference = EE3925F4209E17ECF3E692D1 /* Pods-example-tvOS-example-tvOSTests.release.xcconfig */;
753
+			buildSettings = {
754
+				BUNDLE_LOADER = "$(TEST_HOST)";
755
+				CLANG_ANALYZER_NONNULL = YES;
756
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
757
+				CLANG_WARN_INFINITE_RECURSION = YES;
758
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
759
+				COPY_PHASE_STRIP = NO;
760
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
761
+				GCC_NO_COMMON_BLOCKS = YES;
762
+				INFOPLIST_FILE = "example-tvOSTests/Info.plist";
763
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
764
+				OTHER_LDFLAGS = (
765
+					"$(inherited)",
766
+					"-ObjC",
767
+					"-lc++",
768
+				);
769
+				PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.example-tvOSTests";
770
+				PRODUCT_NAME = "$(TARGET_NAME)";
771
+				SDKROOT = appletvos;
772
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example-tvOS.app/example-tvOS";
773
+				TVOS_DEPLOYMENT_TARGET = 10.1;
774
+			};
775
+			name = Release;
776
+		};
777
+		83CBBA201A601CBA00E9B192 /* Debug */ = {
778
+			isa = XCBuildConfiguration;
779
+			buildSettings = {
780
+				ALWAYS_SEARCH_USER_PATHS = NO;
781
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
782
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
783
+				CLANG_CXX_LIBRARY = "libc++";
784
+				CLANG_ENABLE_MODULES = YES;
785
+				CLANG_ENABLE_OBJC_ARC = YES;
786
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
787
+				CLANG_WARN_BOOL_CONVERSION = YES;
788
+				CLANG_WARN_COMMA = YES;
789
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
790
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
791
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
792
+				CLANG_WARN_EMPTY_BODY = YES;
793
+				CLANG_WARN_ENUM_CONVERSION = YES;
794
+				CLANG_WARN_INFINITE_RECURSION = YES;
795
+				CLANG_WARN_INT_CONVERSION = YES;
796
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
797
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
798
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
799
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
800
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
801
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
802
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
803
+				CLANG_WARN_UNREACHABLE_CODE = YES;
804
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
805
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
806
+				COPY_PHASE_STRIP = NO;
807
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
808
+				ENABLE_TESTABILITY = YES;
809
+				GCC_C_LANGUAGE_STANDARD = gnu99;
810
+				GCC_DYNAMIC_NO_PIC = NO;
811
+				GCC_NO_COMMON_BLOCKS = YES;
812
+				GCC_OPTIMIZATION_LEVEL = 0;
813
+				GCC_PREPROCESSOR_DEFINITIONS = (
814
+					"DEBUG=1",
815
+					"$(inherited)",
816
+				);
817
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
818
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
819
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
820
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
821
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
822
+				GCC_WARN_UNUSED_FUNCTION = YES;
823
+				GCC_WARN_UNUSED_VARIABLE = YES;
824
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
825
+				MTL_ENABLE_DEBUG_INFO = YES;
826
+				ONLY_ACTIVE_ARCH = YES;
827
+				SDKROOT = iphoneos;
828
+			};
829
+			name = Debug;
830
+		};
831
+		83CBBA211A601CBA00E9B192 /* Release */ = {
832
+			isa = XCBuildConfiguration;
833
+			buildSettings = {
834
+				ALWAYS_SEARCH_USER_PATHS = NO;
835
+				CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
836
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
837
+				CLANG_CXX_LIBRARY = "libc++";
838
+				CLANG_ENABLE_MODULES = YES;
839
+				CLANG_ENABLE_OBJC_ARC = YES;
840
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
841
+				CLANG_WARN_BOOL_CONVERSION = YES;
842
+				CLANG_WARN_COMMA = YES;
843
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
844
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
845
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
846
+				CLANG_WARN_EMPTY_BODY = YES;
847
+				CLANG_WARN_ENUM_CONVERSION = YES;
848
+				CLANG_WARN_INFINITE_RECURSION = YES;
849
+				CLANG_WARN_INT_CONVERSION = YES;
850
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
851
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
852
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
853
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
854
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
855
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
856
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
857
+				CLANG_WARN_UNREACHABLE_CODE = YES;
858
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
859
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
860
+				COPY_PHASE_STRIP = YES;
861
+				ENABLE_NS_ASSERTIONS = NO;
862
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
863
+				GCC_C_LANGUAGE_STANDARD = gnu99;
864
+				GCC_NO_COMMON_BLOCKS = YES;
865
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
866
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
867
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
868
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
869
+				GCC_WARN_UNUSED_FUNCTION = YES;
870
+				GCC_WARN_UNUSED_VARIABLE = YES;
871
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
872
+				MTL_ENABLE_DEBUG_INFO = NO;
873
+				SDKROOT = iphoneos;
874
+				VALIDATE_PRODUCT = YES;
875
+			};
876
+			name = Release;
877
+		};
878
+/* End XCBuildConfiguration section */
879
+
880
+/* Begin XCConfigurationList section */
881
+		00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "exampleTests" */ = {
882
+			isa = XCConfigurationList;
883
+			buildConfigurations = (
884
+				00E356F61AD99517003FC87E /* Debug */,
885
+				00E356F71AD99517003FC87E /* Release */,
886
+			);
887
+			defaultConfigurationIsVisible = 0;
888
+			defaultConfigurationName = Release;
889
+		};
890
+		13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */ = {
891
+			isa = XCConfigurationList;
892
+			buildConfigurations = (
893
+				13B07F941A680F5B00A75B9A /* Debug */,
894
+				13B07F951A680F5B00A75B9A /* Release */,
895
+			);
896
+			defaultConfigurationIsVisible = 0;
897
+			defaultConfigurationName = Release;
898
+		};
899
+		2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "example-tvOS" */ = {
900
+			isa = XCConfigurationList;
901
+			buildConfigurations = (
902
+				2D02E4971E0B4A5E006451C7 /* Debug */,
903
+				2D02E4981E0B4A5E006451C7 /* Release */,
904
+			);
905
+			defaultConfigurationIsVisible = 0;
906
+			defaultConfigurationName = Release;
907
+		};
908
+		2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "example-tvOSTests" */ = {
909
+			isa = XCConfigurationList;
910
+			buildConfigurations = (
911
+				2D02E4991E0B4A5E006451C7 /* Debug */,
912
+				2D02E49A1E0B4A5E006451C7 /* Release */,
913
+			);
914
+			defaultConfigurationIsVisible = 0;
915
+			defaultConfigurationName = Release;
916
+		};
917
+		83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "example" */ = {
918
+			isa = XCConfigurationList;
919
+			buildConfigurations = (
920
+				83CBBA201A601CBA00E9B192 /* Debug */,
921
+				83CBBA211A601CBA00E9B192 /* Release */,
922
+			);
923
+			defaultConfigurationIsVisible = 0;
924
+			defaultConfigurationName = Release;
925
+		};
926
+/* End XCConfigurationList section */
927
+	};
928
+	rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
929
+}

+ 88
- 0
example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme 查看文件

@@ -0,0 +1,88 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Scheme
3
+   LastUpgradeVersion = "1130"
4
+   version = "1.3">
5
+   <BuildAction
6
+      parallelizeBuildables = "YES"
7
+      buildImplicitDependencies = "YES">
8
+      <BuildActionEntries>
9
+         <BuildActionEntry
10
+            buildForTesting = "YES"
11
+            buildForRunning = "YES"
12
+            buildForProfiling = "YES"
13
+            buildForArchiving = "YES"
14
+            buildForAnalyzing = "YES">
15
+            <BuildableReference
16
+               BuildableIdentifier = "primary"
17
+               BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
18
+               BuildableName = "example-tvOS.app"
19
+               BlueprintName = "example-tvOS"
20
+               ReferencedContainer = "container:example.xcodeproj">
21
+            </BuildableReference>
22
+         </BuildActionEntry>
23
+      </BuildActionEntries>
24
+   </BuildAction>
25
+   <TestAction
26
+      buildConfiguration = "Debug"
27
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29
+      shouldUseLaunchSchemeArgsEnv = "YES">
30
+      <Testables>
31
+         <TestableReference
32
+            skipped = "NO">
33
+            <BuildableReference
34
+               BuildableIdentifier = "primary"
35
+               BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
36
+               BuildableName = "example-tvOSTests.xctest"
37
+               BlueprintName = "example-tvOSTests"
38
+               ReferencedContainer = "container:example.xcodeproj">
39
+            </BuildableReference>
40
+         </TestableReference>
41
+      </Testables>
42
+   </TestAction>
43
+   <LaunchAction
44
+      buildConfiguration = "Debug"
45
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
46
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
47
+      launchStyle = "0"
48
+      useCustomWorkingDirectory = "NO"
49
+      ignoresPersistentStateOnLaunch = "NO"
50
+      debugDocumentVersioning = "YES"
51
+      debugServiceExtension = "internal"
52
+      allowLocationSimulation = "YES">
53
+      <BuildableProductRunnable
54
+         runnableDebuggingMode = "0">
55
+         <BuildableReference
56
+            BuildableIdentifier = "primary"
57
+            BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
58
+            BuildableName = "example-tvOS.app"
59
+            BlueprintName = "example-tvOS"
60
+            ReferencedContainer = "container:example.xcodeproj">
61
+         </BuildableReference>
62
+      </BuildableProductRunnable>
63
+   </LaunchAction>
64
+   <ProfileAction
65
+      buildConfiguration = "Release"
66
+      shouldUseLaunchSchemeArgsEnv = "YES"
67
+      savedToolIdentifier = ""
68
+      useCustomWorkingDirectory = "NO"
69
+      debugDocumentVersioning = "YES">
70
+      <BuildableProductRunnable
71
+         runnableDebuggingMode = "0">
72
+         <BuildableReference
73
+            BuildableIdentifier = "primary"
74
+            BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
75
+            BuildableName = "example-tvOS.app"
76
+            BlueprintName = "example-tvOS"
77
+            ReferencedContainer = "container:example.xcodeproj">
78
+         </BuildableReference>
79
+      </BuildableProductRunnable>
80
+   </ProfileAction>
81
+   <AnalyzeAction
82
+      buildConfiguration = "Debug">
83
+   </AnalyzeAction>
84
+   <ArchiveAction
85
+      buildConfiguration = "Release"
86
+      revealArchiveInOrganizer = "YES">
87
+   </ArchiveAction>
88
+</Scheme>

+ 88
- 0
example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme 查看文件

@@ -0,0 +1,88 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Scheme
3
+   LastUpgradeVersion = "1130"
4
+   version = "1.3">
5
+   <BuildAction
6
+      parallelizeBuildables = "YES"
7
+      buildImplicitDependencies = "YES">
8
+      <BuildActionEntries>
9
+         <BuildActionEntry
10
+            buildForTesting = "YES"
11
+            buildForRunning = "YES"
12
+            buildForProfiling = "YES"
13
+            buildForArchiving = "YES"
14
+            buildForAnalyzing = "YES">
15
+            <BuildableReference
16
+               BuildableIdentifier = "primary"
17
+               BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
18
+               BuildableName = "example.app"
19
+               BlueprintName = "example"
20
+               ReferencedContainer = "container:example.xcodeproj">
21
+            </BuildableReference>
22
+         </BuildActionEntry>
23
+      </BuildActionEntries>
24
+   </BuildAction>
25
+   <TestAction
26
+      buildConfiguration = "Debug"
27
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
28
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
29
+      shouldUseLaunchSchemeArgsEnv = "YES">
30
+      <Testables>
31
+         <TestableReference
32
+            skipped = "NO">
33
+            <BuildableReference
34
+               BuildableIdentifier = "primary"
35
+               BlueprintIdentifier = "00E356ED1AD99517003FC87E"
36
+               BuildableName = "exampleTests.xctest"
37
+               BlueprintName = "exampleTests"
38
+               ReferencedContainer = "container:example.xcodeproj">
39
+            </BuildableReference>
40
+         </TestableReference>
41
+      </Testables>
42
+   </TestAction>
43
+   <LaunchAction
44
+      buildConfiguration = "Debug"
45
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
46
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
47
+      launchStyle = "0"
48
+      useCustomWorkingDirectory = "NO"
49
+      ignoresPersistentStateOnLaunch = "NO"
50
+      debugDocumentVersioning = "YES"
51
+      debugServiceExtension = "internal"
52
+      allowLocationSimulation = "YES">
53
+      <BuildableProductRunnable
54
+         runnableDebuggingMode = "0">
55
+         <BuildableReference
56
+            BuildableIdentifier = "primary"
57
+            BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
58
+            BuildableName = "example.app"
59
+            BlueprintName = "example"
60
+            ReferencedContainer = "container:example.xcodeproj">
61
+         </BuildableReference>
62
+      </BuildableProductRunnable>
63
+   </LaunchAction>
64
+   <ProfileAction
65
+      buildConfiguration = "Release"
66
+      shouldUseLaunchSchemeArgsEnv = "YES"
67
+      savedToolIdentifier = ""
68
+      useCustomWorkingDirectory = "NO"
69
+      debugDocumentVersioning = "YES">
70
+      <BuildableProductRunnable
71
+         runnableDebuggingMode = "0">
72
+         <BuildableReference
73
+            BuildableIdentifier = "primary"
74
+            BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
75
+            BuildableName = "example.app"
76
+            BlueprintName = "example"
77
+            ReferencedContainer = "container:example.xcodeproj">
78
+         </BuildableReference>
79
+      </BuildableProductRunnable>
80
+   </ProfileAction>
81
+   <AnalyzeAction
82
+      buildConfiguration = "Debug">
83
+   </AnalyzeAction>
84
+   <ArchiveAction
85
+      buildConfiguration = "Release"
86
+      revealArchiveInOrganizer = "YES">
87
+   </ArchiveAction>
88
+</Scheme>

+ 10
- 0
example/ios/example.xcworkspace/contents.xcworkspacedata 查看文件

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Workspace
3
+   version = "1.0">
4
+   <FileRef
5
+      location = "group:example.xcodeproj">
6
+   </FileRef>
7
+   <FileRef
8
+      location = "group:Pods/Pods.xcodeproj">
9
+   </FileRef>
10
+</Workspace>

+ 8
- 0
example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist 查看文件

@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>IDEDidComputeMac32BitWarning</key>
6
+	<true/>
7
+</dict>
8
+</plist>

+ 15
- 0
example/ios/example/AppDelegate.h 查看文件

@@ -0,0 +1,15 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+#import <React/RCTBridgeDelegate.h>
9
+#import <UIKit/UIKit.h>
10
+
11
+@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
12
+
13
+@property (nonatomic, strong) UIWindow *window;
14
+
15
+@end

+ 42
- 0
example/ios/example/AppDelegate.m 查看文件

@@ -0,0 +1,42 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+#import "AppDelegate.h"
9
+
10
+#import <React/RCTBridge.h>
11
+#import <React/RCTBundleURLProvider.h>
12
+#import <React/RCTRootView.h>
13
+
14
+@implementation AppDelegate
15
+
16
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
17
+{
18
+  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
19
+  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
20
+                                                   moduleName:@"example"
21
+                                            initialProperties:nil];
22
+
23
+  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
24
+
25
+  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
26
+  UIViewController *rootViewController = [UIViewController new];
27
+  rootViewController.view = rootView;
28
+  self.window.rootViewController = rootViewController;
29
+  [self.window makeKeyAndVisible];
30
+  return YES;
31
+}
32
+
33
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
34
+{
35
+#if DEBUG
36
+  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"example/index" fallbackResource:nil];
37
+#else
38
+  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
39
+#endif
40
+}
41
+
42
+@end

+ 42
- 0
example/ios/example/Base.lproj/LaunchScreen.xib 查看文件

@@ -0,0 +1,42 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
3
+    <dependencies>
4
+        <deployment identifier="iOS"/>
5
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
6
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
7
+    </dependencies>
8
+    <objects>
9
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
10
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
11
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
12
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
13
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
14
+            <subviews>
15
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
16
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
17
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
18
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
19
+                    <nil key="highlightedColor"/>
20
+                </label>
21
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="example" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
22
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
23
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
24
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
25
+                    <nil key="highlightedColor"/>
26
+                </label>
27
+            </subviews>
28
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
29
+            <constraints>
30
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
31
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
32
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
33
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
34
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
35
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
36
+            </constraints>
37
+            <nil key="simulatedStatusBarMetrics"/>
38
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
39
+            <point key="canvasLocation" x="548" y="455"/>
40
+        </view>
41
+    </objects>
42
+</document>

+ 38
- 0
example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json 查看文件

@@ -0,0 +1,38 @@
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
+}

+ 6
- 0
example/ios/example/Images.xcassets/Contents.json 查看文件

@@ -0,0 +1,6 @@
1
+{
2
+  "info" : {
3
+    "version" : 1,
4
+    "author" : "xcode"
5
+  }
6
+}

+ 57
- 0
example/ios/example/Info.plist 查看文件

@@ -0,0 +1,57 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleDisplayName</key>
8
+	<string>example</string>
9
+	<key>CFBundleExecutable</key>
10
+	<string>$(EXECUTABLE_NAME)</string>
11
+	<key>CFBundleIdentifier</key>
12
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13
+	<key>CFBundleInfoDictionaryVersion</key>
14
+	<string>6.0</string>
15
+	<key>CFBundleName</key>
16
+	<string>$(PRODUCT_NAME)</string>
17
+	<key>CFBundlePackageType</key>
18
+	<string>APPL</string>
19
+	<key>CFBundleShortVersionString</key>
20
+	<string>1.0</string>
21
+	<key>CFBundleSignature</key>
22
+	<string>????</string>
23
+	<key>CFBundleVersion</key>
24
+	<string>1</string>
25
+	<key>LSRequiresIPhoneOS</key>
26
+	<true/>
27
+	<key>NSAppTransportSecurity</key>
28
+	<dict>
29
+		<key>NSAllowsArbitraryLoads</key>
30
+		<true/>
31
+		<key>NSExceptionDomains</key>
32
+		<dict>
33
+			<key>localhost</key>
34
+			<dict>
35
+				<key>NSExceptionAllowsInsecureHTTPLoads</key>
36
+				<true/>
37
+			</dict>
38
+		</dict>
39
+	</dict>
40
+	<key>NSLocationWhenInUseUsageDescription</key>
41
+	<string></string>
42
+	<key>UILaunchStoryboardName</key>
43
+	<string>LaunchScreen</string>
44
+	<key>UIRequiredDeviceCapabilities</key>
45
+	<array>
46
+		<string>armv7</string>
47
+	</array>
48
+	<key>UISupportedInterfaceOrientations</key>
49
+	<array>
50
+		<string>UIInterfaceOrientationPortrait</string>
51
+		<string>UIInterfaceOrientationLandscapeLeft</string>
52
+		<string>UIInterfaceOrientationLandscapeRight</string>
53
+	</array>
54
+	<key>UIViewControllerBasedStatusBarAppearance</key>
55
+	<false/>
56
+</dict>
57
+</plist>

+ 16
- 0
example/ios/example/main.m 查看文件

@@ -0,0 +1,16 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+#import <UIKit/UIKit.h>
9
+
10
+#import "AppDelegate.h"
11
+
12
+int main(int argc, char * argv[]) {
13
+  @autoreleasepool {
14
+    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15
+  }
16
+}

+ 24
- 0
example/ios/exampleTests/Info.plist 查看文件

@@ -0,0 +1,24 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleExecutable</key>
8
+	<string>$(EXECUTABLE_NAME)</string>
9
+	<key>CFBundleIdentifier</key>
10
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
11
+	<key>CFBundleInfoDictionaryVersion</key>
12
+	<string>6.0</string>
13
+	<key>CFBundleName</key>
14
+	<string>$(PRODUCT_NAME)</string>
15
+	<key>CFBundlePackageType</key>
16
+	<string>BNDL</string>
17
+	<key>CFBundleShortVersionString</key>
18
+	<string>1.0</string>
19
+	<key>CFBundleSignature</key>
20
+	<string>????</string>
21
+	<key>CFBundleVersion</key>
22
+	<string>1</string>
23
+</dict>
24
+</plist>

+ 72
- 0
example/ios/exampleTests/exampleTests.m 查看文件

@@ -0,0 +1,72 @@
1
+/**
2
+ * Copyright (c) Facebook, Inc. and its affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ */
7
+
8
+#import <UIKit/UIKit.h>
9
+#import <XCTest/XCTest.h>
10
+
11
+#import <React/RCTLog.h>
12
+#import <React/RCTRootView.h>
13
+
14
+#define TIMEOUT_SECONDS 600
15
+#define TEXT_TO_LOOK_FOR @"Welcome to React"
16
+
17
+@interface exampleTests : XCTestCase
18
+
19
+@end
20
+
21
+@implementation exampleTests
22
+
23
+- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test
24
+{
25
+  if (test(view)) {
26
+    return YES;
27
+  }
28
+  for (UIView *subview in [view subviews]) {
29
+    if ([self findSubviewInView:subview matching:test]) {
30
+      return YES;
31
+    }
32
+  }
33
+  return NO;
34
+}
35
+
36
+- (void)testRendersWelcomeScreen
37
+{
38
+  UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
39
+  NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
40
+  BOOL foundElement = NO;
41
+
42
+  __block NSString *redboxError = nil;
43
+#ifdef DEBUG
44
+  RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
45
+    if (level >= RCTLogLevelError) {
46
+      redboxError = message;
47
+    }
48
+  });
49
+#endif
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
+#ifdef DEBUG
64
+  RCTSetLogFunction(RCTDefaultLogFunction);
65
+#endif
66
+
67
+  XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
68
+  XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
69
+}
70
+
71
+
72
+@end

+ 42
- 0
example/macos/Podfile 查看文件

@@ -0,0 +1,42 @@
1
+require_relative '../../node_modules/@react-native-community/cli-platform-ios/native_modules'
2
+
3
+abstract_target 'Shared' do
4
+  use_native_modules!
5
+
6
+  pod 'react-native-webview', :path => "../.."
7
+
8
+  pod 'React', :path => "../../node_modules/react-native-macos/"
9
+  pod 'React-Core', :path => "../../node_modules/react-native-macos/React"
10
+  pod 'React-fishhook', :path => "../../node_modules/react-native-macos/Libraries/fishhook"
11
+  pod 'React-RCTActionSheet', :path => "../../node_modules/react-native-macos/Libraries/ActionSheetIOS"
12
+  pod 'React-RCTAnimation', :path => "../../node_modules/react-native-macos/Libraries/NativeAnimation"
13
+  pod 'React-RCTBlob', :path => "../../node_modules/react-native-macos/Libraries/Blob"
14
+  pod 'React-RCTImage', :path => "../../node_modules/react-native-macos/Libraries/Image"
15
+  pod 'React-RCTLinking', :path => "../../node_modules/react-native-macos/Libraries/LinkingIOS"
16
+  pod 'React-RCTNetwork', :path => "../../node_modules/react-native-macos/Libraries/Network"
17
+  pod 'React-RCTSettings', :path => "../../node_modules/react-native-macos/Libraries/Settings"
18
+  pod 'React-RCTText', :path => "../../node_modules/react-native-macos/Libraries/Text"
19
+  pod 'React-RCTVibration', :path => "../../node_modules/react-native-macos/Libraries/Vibration"
20
+  pod 'React-RCTWebSocket', :path => "../../node_modules/react-native-macos/Libraries/WebSocket"
21
+  pod 'React-cxxreact', :path => "../../node_modules/react-native-macos/ReactCommon/cxxreact"
22
+  pod 'React-jscallinvoker', :path => "../../node_modules/react-native-macos/ReactCommon/jscallinvoker"
23
+  pod 'React-jsi', :path => "../../node_modules/react-native-macos/ReactCommon/jsi"
24
+  pod 'React-jsiexecutor', :path => "../../node_modules/react-native-macos/ReactCommon/jsiexecutor"
25
+  pod 'React-jsinspector', :path => "../../node_modules/react-native-macos/ReactCommon/jsinspector"
26
+  pod 'yoga', :path => "../../node_modules/react-native-macos/ReactCommon/yoga"
27
+  pod 'DoubleConversion', :podspec => "../../node_modules/react-native-macos/third-party-podspecs/DoubleConversion.podspec"
28
+  pod 'glog', :podspec => "../../node_modules/react-native-macos/third-party-podspecs/glog.podspec"
29
+  pod 'Folly', :podspec => "../../node_modules/react-native-macos/third-party-podspecs/Folly.podspec"
30
+  pod 'boost-for-react-native', :podspec => "../../node_modules/react-native-macos/third-party-podspecs/boost-for-react-native.podspec"
31
+  pod 'React-DevSupport', :path => "../../node_modules/react-native-macos/React"
32
+
33
+  target 'example-macOS' do
34
+    platform :macos, '10.14'
35
+    # Pods specifically for macOS target
36
+  end
37
+
38
+  target 'example-iOS' do
39
+    platform :ios, '9'
40
+    # Pods specifically for iOS target
41
+  end
42
+end

+ 199
- 0
example/macos/Podfile.lock 查看文件

@@ -0,0 +1,199 @@
1
+PODS:
2
+  - boost-for-react-native (1.63.0)
3
+  - DoubleConversion (1.1.6)
4
+  - Folly (2018.10.22.00):
5
+    - boost-for-react-native
6
+    - DoubleConversion
7
+    - Folly/Default (= 2018.10.22.00)
8
+    - glog
9
+  - Folly/Default (2018.10.22.00):
10
+    - boost-for-react-native
11
+    - DoubleConversion
12
+    - glog
13
+  - glog (0.3.5)
14
+  - React (0.60.0-microsoft.73):
15
+    - React-Core (= 0.60.0-microsoft.73)
16
+    - React-DevSupport (= 0.60.0-microsoft.73)
17
+    - React-RCTActionSheet (= 0.60.0-microsoft.73)
18
+    - React-RCTAnimation (= 0.60.0-microsoft.73)
19
+    - React-RCTBlob (= 0.60.0-microsoft.73)
20
+    - React-RCTImage (= 0.60.0-microsoft.73)
21
+    - React-RCTLinking (= 0.60.0-microsoft.73)
22
+    - React-RCTNetwork (= 0.60.0-microsoft.73)
23
+    - React-RCTSettings (= 0.60.0-microsoft.73)
24
+    - React-RCTText (= 0.60.0-microsoft.73)
25
+    - React-RCTVibration (= 0.60.0-microsoft.73)
26
+    - React-RCTWebSocket (= 0.60.0-microsoft.73)
27
+  - React-Core (0.60.0-microsoft.73):
28
+    - Folly (= 2018.10.22.00)
29
+    - React-cxxreact (= 0.60.0-microsoft.73)
30
+    - React-jsiexecutor (= 0.60.0-microsoft.73)
31
+    - yoga (= 0.60.0-microsoft.73.React)
32
+  - React-cxxreact (0.60.0-microsoft.73):
33
+    - boost-for-react-native (= 1.63.0)
34
+    - DoubleConversion
35
+    - Folly (= 2018.10.22.00)
36
+    - glog
37
+    - React-jsinspector (= 0.60.0-microsoft.73)
38
+  - React-DevSupport (0.60.0-microsoft.73):
39
+    - React-Core (= 0.60.0-microsoft.73)
40
+    - React-RCTWebSocket (= 0.60.0-microsoft.73)
41
+  - React-fishhook (0.60.0-microsoft.73)
42
+  - React-jscallinvoker (0.60.0-microsoft.73):
43
+    - Folly (= 2018.10.22.00)
44
+    - React-cxxreact (= 0.60.0-microsoft.73)
45
+  - React-jsi (0.60.0-microsoft.73):
46
+    - boost-for-react-native (= 1.63.0)
47
+    - DoubleConversion
48
+    - Folly (= 2018.10.22.00)
49
+    - glog
50
+    - React-jsi/Default (= 0.60.0-microsoft.73)
51
+  - React-jsi/Default (0.60.0-microsoft.73):
52
+    - boost-for-react-native (= 1.63.0)
53
+    - DoubleConversion
54
+    - Folly (= 2018.10.22.00)
55
+    - glog
56
+  - React-jsiexecutor (0.60.0-microsoft.73):
57
+    - DoubleConversion
58
+    - Folly (= 2018.10.22.00)
59
+    - glog
60
+    - React-cxxreact (= 0.60.0-microsoft.73)
61
+    - React-jsi (= 0.60.0-microsoft.73)
62
+  - React-jsinspector (0.60.0-microsoft.73)
63
+  - react-native-webview (9.2.1):
64
+    - React
65
+  - React-RCTActionSheet (0.60.0-microsoft.73):
66
+    - React-Core (= 0.60.0-microsoft.73)
67
+  - React-RCTAnimation (0.60.0-microsoft.73):
68
+    - React-Core (= 0.60.0-microsoft.73)
69
+  - React-RCTBlob (0.60.0-microsoft.73):
70
+    - React-Core (= 0.60.0-microsoft.73)
71
+    - React-RCTNetwork (= 0.60.0-microsoft.73)
72
+    - React-RCTWebSocket (= 0.60.0-microsoft.73)
73
+  - React-RCTImage (0.60.0-microsoft.73):
74
+    - React-Core (= 0.60.0-microsoft.73)
75
+    - React-RCTNetwork (= 0.60.0-microsoft.73)
76
+  - React-RCTLinking (0.60.0-microsoft.73):
77
+    - React-Core (= 0.60.0-microsoft.73)
78
+  - React-RCTNetwork (0.60.0-microsoft.73):
79
+    - React-Core (= 0.60.0-microsoft.73)
80
+  - React-RCTSettings (0.60.0-microsoft.73):
81
+    - React-Core (= 0.60.0-microsoft.73)
82
+  - React-RCTText (0.60.0-microsoft.73):
83
+    - React-Core (= 0.60.0-microsoft.73)
84
+  - React-RCTVibration (0.60.0-microsoft.73):
85
+    - React-Core (= 0.60.0-microsoft.73)
86
+  - React-RCTWebSocket (0.60.0-microsoft.73):
87
+    - React-Core (= 0.60.0-microsoft.73)
88
+    - React-fishhook (= 0.60.0-microsoft.73)
89
+  - yoga (0.60.0-microsoft.73.React)
90
+
91
+DEPENDENCIES:
92
+  - boost-for-react-native (from `../../node_modules/react-native-macos/third-party-podspecs/boost-for-react-native.podspec`)
93
+  - DoubleConversion (from `../../node_modules/react-native-macos/third-party-podspecs/DoubleConversion.podspec`)
94
+  - Folly (from `../../node_modules/react-native-macos/third-party-podspecs/Folly.podspec`)
95
+  - glog (from `../../node_modules/react-native-macos/third-party-podspecs/glog.podspec`)
96
+  - React (from `../../node_modules/react-native-macos/`)
97
+  - React-Core (from `../../node_modules/react-native-macos/React`)
98
+  - React-cxxreact (from `../../node_modules/react-native-macos/ReactCommon/cxxreact`)
99
+  - React-DevSupport (from `../../node_modules/react-native-macos/React`)
100
+  - React-fishhook (from `../../node_modules/react-native-macos/Libraries/fishhook`)
101
+  - React-jscallinvoker (from `../../node_modules/react-native-macos/ReactCommon/jscallinvoker`)
102
+  - React-jsi (from `../../node_modules/react-native-macos/ReactCommon/jsi`)
103
+  - React-jsiexecutor (from `../../node_modules/react-native-macos/ReactCommon/jsiexecutor`)
104
+  - React-jsinspector (from `../../node_modules/react-native-macos/ReactCommon/jsinspector`)
105
+  - react-native-webview (from `../..`)
106
+  - React-RCTActionSheet (from `../../node_modules/react-native-macos/Libraries/ActionSheetIOS`)
107
+  - React-RCTAnimation (from `../../node_modules/react-native-macos/Libraries/NativeAnimation`)
108
+  - React-RCTBlob (from `../../node_modules/react-native-macos/Libraries/Blob`)
109
+  - React-RCTImage (from `../../node_modules/react-native-macos/Libraries/Image`)
110
+  - React-RCTLinking (from `../../node_modules/react-native-macos/Libraries/LinkingIOS`)
111
+  - React-RCTNetwork (from `../../node_modules/react-native-macos/Libraries/Network`)
112
+  - React-RCTSettings (from `../../node_modules/react-native-macos/Libraries/Settings`)
113
+  - React-RCTText (from `../../node_modules/react-native-macos/Libraries/Text`)
114
+  - React-RCTVibration (from `../../node_modules/react-native-macos/Libraries/Vibration`)
115
+  - React-RCTWebSocket (from `../../node_modules/react-native-macos/Libraries/WebSocket`)
116
+  - yoga (from `../../node_modules/react-native-macos/ReactCommon/yoga`)
117
+
118
+EXTERNAL SOURCES:
119
+  boost-for-react-native:
120
+    :podspec: "../../node_modules/react-native-macos/third-party-podspecs/boost-for-react-native.podspec"
121
+  DoubleConversion:
122
+    :podspec: "../../node_modules/react-native-macos/third-party-podspecs/DoubleConversion.podspec"
123
+  Folly:
124
+    :podspec: "../../node_modules/react-native-macos/third-party-podspecs/Folly.podspec"
125
+  glog:
126
+    :podspec: "../../node_modules/react-native-macos/third-party-podspecs/glog.podspec"
127
+  React:
128
+    :path: "../../node_modules/react-native-macos/"
129
+  React-Core:
130
+    :path: "../../node_modules/react-native-macos/React"
131
+  React-cxxreact:
132
+    :path: "../../node_modules/react-native-macos/ReactCommon/cxxreact"
133
+  React-DevSupport:
134
+    :path: "../../node_modules/react-native-macos/React"
135
+  React-fishhook:
136
+    :path: "../../node_modules/react-native-macos/Libraries/fishhook"
137
+  React-jscallinvoker:
138
+    :path: "../../node_modules/react-native-macos/ReactCommon/jscallinvoker"
139
+  React-jsi:
140
+    :path: "../../node_modules/react-native-macos/ReactCommon/jsi"
141
+  React-jsiexecutor:
142
+    :path: "../../node_modules/react-native-macos/ReactCommon/jsiexecutor"
143
+  React-jsinspector:
144
+    :path: "../../node_modules/react-native-macos/ReactCommon/jsinspector"
145
+  react-native-webview:
146
+    :path: "../.."
147
+  React-RCTActionSheet:
148
+    :path: "../../node_modules/react-native-macos/Libraries/ActionSheetIOS"
149
+  React-RCTAnimation:
150
+    :path: "../../node_modules/react-native-macos/Libraries/NativeAnimation"
151
+  React-RCTBlob:
152
+    :path: "../../node_modules/react-native-macos/Libraries/Blob"
153
+  React-RCTImage:
154
+    :path: "../../node_modules/react-native-macos/Libraries/Image"
155
+  React-RCTLinking:
156
+    :path: "../../node_modules/react-native-macos/Libraries/LinkingIOS"
157
+  React-RCTNetwork:
158
+    :path: "../../node_modules/react-native-macos/Libraries/Network"
159
+  React-RCTSettings:
160
+    :path: "../../node_modules/react-native-macos/Libraries/Settings"
161
+  React-RCTText:
162
+    :path: "../../node_modules/react-native-macos/Libraries/Text"
163
+  React-RCTVibration:
164
+    :path: "../../node_modules/react-native-macos/Libraries/Vibration"
165
+  React-RCTWebSocket:
166
+    :path: "../../node_modules/react-native-macos/Libraries/WebSocket"
167
+  yoga:
168
+    :path: "../../node_modules/react-native-macos/ReactCommon/yoga"
169
+
170
+SPEC CHECKSUMS:
171
+  boost-for-react-native: a110407d9db2642fd2e1bcd7c5a51c81f2521dc9
172
+  DoubleConversion: a1bc12a74baa397a2609e0f10e19b8062d864053
173
+  Folly: feff29ba9d0b7c2e4f793a94942831d6cc5bbad7
174
+  glog: b3f6d74f3e2d33396addc0ee724d2b2b79fc3e00
175
+  React: 4d79a1cdf3230e04aca581c25efee08da1ee5164
176
+  React-Core: 13ba8f1abdf6bcd5ff0f483a9ef7a2db9a107f0e
177
+  React-cxxreact: 11924907362f4cac7420b882760f939eb126ea26
178
+  React-DevSupport: ad7a5fc590659aeccfbf865fc4a134b70436d648
179
+  React-fishhook: 4990f5c5822d79c7ab3fb76c79ac17d09bee9336
180
+  React-jscallinvoker: 3a2e5ca0f66931ede7f55dfa2c3678983b668e86
181
+  React-jsi: 62b3cdf6e9a83d5c18dcf7dc895a362fb4b7853b
182
+  React-jsiexecutor: 8bd40a456b96ac807634225efa05ac2c35894dd0
183
+  React-jsinspector: c549193caebd0004cf2df489c57c5a24614c5516
184
+  react-native-webview: d75854c6508447f78d548dbdfbfc9a2049c3b1c5
185
+  React-RCTActionSheet: 7d7c2282c5a782e7a13f8da967eb2fe6ba296847
186
+  React-RCTAnimation: 289ab0e35a77a2f585fd2743b6abf7d52fac7a5b
187
+  React-RCTBlob: fff2b91fe158a258b72e610d5e0f77db4e059bef
188
+  React-RCTImage: cc560497ac826ca385dff28e631233ec8d1b95a6
189
+  React-RCTLinking: 6d4ed366a86755fba71a0446e5ee33e9944d8c0a
190
+  React-RCTNetwork: 15ce6970143dd7883db946b97d822098a1f9fc98
191
+  React-RCTSettings: 4729f1553af00f2f073a3e4a9743e6d35578afd6
192
+  React-RCTText: e5c12c5085188057d606c6627d8022c9983226b8
193
+  React-RCTVibration: 19f2176d53ccf811cc56dc333e538ae2f3a3822c
194
+  React-RCTWebSocket: ed4f366970f1cb6280a2edf6402aaebb224cd5ef
195
+  yoga: 94752fd7c7be3ab04256ef648abc656f52d77501
196
+
197
+PODFILE CHECKSUM: 57ba7807ee34b3b25a5ef3497860eaf96be05af7
198
+
199
+COCOAPODS: 1.8.4

+ 8
- 0
example/macos/example-iOS/AppDelegate.h 查看文件

@@ -0,0 +1,8 @@
1
+#import <React/RCTBridgeDelegate.h>
2
+#import <UIKit/UIKit.h>
3
+
4
+@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
5
+
6
+@property (nonatomic, strong) UIWindow *window;
7
+
8
+@end

+ 35
- 0
example/macos/example-iOS/AppDelegate.m 查看文件

@@ -0,0 +1,35 @@
1
+#import "AppDelegate.h"
2
+
3
+#import <React/RCTBridge.h>
4
+#import <React/RCTBundleURLProvider.h>
5
+#import <React/RCTRootView.h>
6
+
7
+@implementation AppDelegate
8
+
9
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
10
+{
11
+  RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
12
+  RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge
13
+                                                   moduleName:@"example"
14
+                                            initialProperties:nil];
15
+
16
+  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
17
+
18
+  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
19
+  UIViewController *rootViewController = [UIViewController new];
20
+  rootViewController.view = rootView;
21
+  self.window.rootViewController = rootViewController;
22
+  [self.window makeKeyAndVisible];
23
+  return YES;
24
+}
25
+
26
+- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
27
+{
28
+#if DEBUG
29
+  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"example/index" fallbackResource:nil];
30
+#else
31
+  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
32
+#endif
33
+}
34
+
35
+@end

+ 42
- 0
example/macos/example-iOS/Base.lproj/LaunchScreen.xib 查看文件

@@ -0,0 +1,42 @@
1
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
3
+    <dependencies>
4
+        <deployment identifier="iOS"/>
5
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
6
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
7
+    </dependencies>
8
+    <objects>
9
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
10
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
11
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
12
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
13
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
14
+            <subviews>
15
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Powered by React Native" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
16
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
17
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
18
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
19
+                    <nil key="highlightedColor"/>
20
+                </label>
21
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="example" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
22
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
23
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
24
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
25
+                    <nil key="highlightedColor"/>
26
+                </label>
27
+            </subviews>
28
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
29
+            <constraints>
30
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
31
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
32
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
33
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
34
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
35
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
36
+            </constraints>
37
+            <nil key="simulatedStatusBarMetrics"/>
38
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
39
+            <point key="canvasLocation" x="548" y="455"/>
40
+        </view>
41
+    </objects>
42
+</document>

+ 38
- 0
example/macos/example-iOS/Images.xcassets/AppIcon.appiconset/Contents.json 查看文件

@@ -0,0 +1,38 @@
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
+}

+ 6
- 0
example/macos/example-iOS/Images.xcassets/Contents.json 查看文件

@@ -0,0 +1,6 @@
1
+{
2
+  "info" : {
3
+    "version" : 1,
4
+    "author" : "xcode"
5
+  }
6
+}

+ 57
- 0
example/macos/example-iOS/Info.plist 查看文件

@@ -0,0 +1,57 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleDisplayName</key>
8
+	<string>$(PRODUCT_NAME)</string>
9
+	<key>CFBundleExecutable</key>
10
+	<string>$(EXECUTABLE_NAME)</string>
11
+	<key>CFBundleIdentifier</key>
12
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13
+	<key>CFBundleInfoDictionaryVersion</key>
14
+	<string>6.0</string>
15
+	<key>CFBundleName</key>
16
+	<string>$(PRODUCT_NAME)</string>
17
+	<key>CFBundlePackageType</key>
18
+	<string>APPL</string>
19
+	<key>CFBundleShortVersionString</key>
20
+	<string>1.0</string>
21
+	<key>CFBundleSignature</key>
22
+	<string>????</string>
23
+	<key>CFBundleVersion</key>
24
+	<string>1</string>
25
+	<key>LSRequiresIPhoneOS</key>
26
+	<true/>
27
+	<key>NSAppTransportSecurity</key>
28
+	<dict>
29
+		<key>NSAllowsArbitraryLoads</key>
30
+		<true/>
31
+		<key>NSExceptionDomains</key>
32
+		<dict>
33
+			<key>localhost</key>
34
+			<dict>
35
+				<key>NSExceptionAllowsInsecureHTTPLoads</key>
36
+				<true/>
37
+			</dict>
38
+		</dict>
39
+	</dict>
40
+	<key>NSLocationWhenInUseUsageDescription</key>
41
+	<string></string>
42
+	<key>UILaunchStoryboardName</key>
43
+	<string>LaunchScreen</string>
44
+	<key>UIRequiredDeviceCapabilities</key>
45
+	<array>
46
+		<string>armv7</string>
47
+	</array>
48
+	<key>UISupportedInterfaceOrientations</key>
49
+	<array>
50
+		<string>UIInterfaceOrientationPortrait</string>
51
+		<string>UIInterfaceOrientationLandscapeLeft</string>
52
+		<string>UIInterfaceOrientationLandscapeRight</string>
53
+	</array>
54
+	<key>UIViewControllerBasedStatusBarAppearance</key>
55
+	<false/>
56
+</dict>
57
+</plist>

+ 9
- 0
example/macos/example-iOS/main.m 查看文件

@@ -0,0 +1,9 @@
1
+#import <UIKit/UIKit.h>
2
+
3
+#import "AppDelegate.h"
4
+
5
+int main(int argc, char * argv[]) {
6
+  @autoreleasepool {
7
+    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
8
+  }
9
+}

+ 9
- 0
example/macos/example-macOS/AppDelegate.h 查看文件

@@ -0,0 +1,9 @@
1
+#import <Cocoa/Cocoa.h>
2
+
3
+@class RCTBridge;
4
+
5
+@interface AppDelegate : NSObject <NSApplicationDelegate>
6
+
7
+@property (nonatomic, readonly) RCTBridge *bridge;
8
+
9
+@end

+ 0
- 0
example/macos/example-macOS/AppDelegate.m 查看文件


部分文件因文件數量過多而無法顯示