瀏覽代碼

Merge commit 'a482a74108' into fix-ios13-scrollIndicatorOffset

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

+ 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:

+ 12
- 0
.gitattributes 查看文件

@@ -0,0 +1,12 @@
1
+* text=auto
2
+
3
+*.bat text eol=crlf
4
+*.def text eol=crlf
5
+*.filters text eol=crlf
6
+*.idl text eol=crlf
7
+*.props text eol=crlf
8
+*.ps1 text eol=crlf 
9
+*.sln text eol=crlf
10
+*.vcxproj text eol=crlf
11
+*.xaml text eol=crlf
12
+

+ 1
- 1
.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:

+ 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
+}

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

@@ -0,0 +1,61 @@
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: Setup MSBuild
19
+      uses: microsoft/setup-msbuild@v1.0.0
20
+      with:
21
+        vs-version: 16.5
22
+
23
+    - name: Check node modules cache
24
+      uses: actions/cache@v1
25
+      id: yarn-cache
26
+      with:
27
+        path: ./node_modules
28
+        key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}
29
+        restore-keys: |
30
+          ${{ runner.os }}-yarn-
31
+
32
+    - name: Install node modules
33
+      if: steps.yarn-cache.outputs.cache-hit != 'true'
34
+      run: yarn --pure-lockfile
35
+    
36
+    - name: yarn build
37
+      if: steps.yarn-cache.outputs.cache-hit == 'true'
38
+      run: |
39
+        yarn build
40
+        yarn tsc
41
+
42
+    - name: Build x64 release
43
+      shell: powershell
44
+      run: npx react-native run-windows --root example --arch x64 --release --no-packager --no-deploy --logging
45
+
46
+    # Workaround for a bug in package searching during deploy.
47
+    # The deploy script only searches windows/{*/bin/x64/Release,Release/*}, but the build step above placed the pakcages at windows/x64/Release.
48
+    # Copy the packages to Windows/Release before deploying.
49
+    - name: Deploy
50
+      shell: powershell
51
+      run: |
52
+        cd example
53
+        Copy-Item -Path windows\x64\Release -Recurse -Destination windows\
54
+        npx react-native run-windows --arch x64 --release --no-build --no-packager
55
+
56
+    - name: Start Appium server
57
+      shell: powershell
58
+      run: Start-Process PowerShell -ArgumentList "yarn appium"
59
+      
60
+    - name: Run tests
61
+      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

+ 10
- 5
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
 ```
@@ -94,8 +97,10 @@ This project follows the [all-contributors](https://github.com/all-contributors/
94 97
 
95 98
 MIT
96 99
 
97
-## Traduções
100
+## Translations
98 101
 
99 102
 This readme is available in:
100 103
 
101 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
 }

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

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

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

@@ -4,18 +4,18 @@ import android.annotation.SuppressLint;
4 4
 import android.annotation.TargetApi;
5 5
 import android.app.DownloadManager;
6 6
 import android.content.Context;
7
-import android.content.Intent;
8 7
 import android.content.pm.ActivityInfo;
9 8
 import android.content.pm.PackageManager;
10 9
 import android.graphics.Bitmap;
11 10
 import android.graphics.Color;
12 11
 import android.Manifest;
12
+import android.net.http.SslError;
13 13
 import android.net.Uri;
14 14
 import android.os.Build;
15 15
 import android.os.Environment;
16
-import androidx.annotation.RequiresApi;
17
-import androidx.core.content.ContextCompat;
16
+import android.os.SystemClock;
18 17
 import android.text.TextUtils;
18
+import android.util.Log;
19 19
 import android.view.Gravity;
20 20
 import android.view.View;
21 21
 import android.view.ViewGroup;
@@ -26,6 +26,8 @@ import android.webkit.CookieManager;
26 26
 import android.webkit.DownloadListener;
27 27
 import android.webkit.GeolocationPermissions;
28 28
 import android.webkit.JavascriptInterface;
29
+import android.webkit.RenderProcessGoneDetail;
30
+import android.webkit.SslErrorHandler;
29 31
 import android.webkit.PermissionRequest;
30 32
 import android.webkit.URLUtil;
31 33
 import android.webkit.ValueCallback;
@@ -37,16 +39,25 @@ import android.webkit.WebView;
37 39
 import android.webkit.WebViewClient;
38 40
 import android.widget.FrameLayout;
39 41
 
42
+import androidx.annotation.Nullable;
43
+import androidx.annotation.RequiresApi;
44
+import androidx.core.content.ContextCompat;
45
+import androidx.core.util.Pair;
46
+
47
+import com.facebook.common.logging.FLog;
40 48
 import com.facebook.react.views.scroll.ScrollEvent;
41 49
 import com.facebook.react.views.scroll.ScrollEventType;
42 50
 import com.facebook.react.views.scroll.OnScrollDispatchHelper;
43 51
 import com.facebook.react.bridge.Arguments;
52
+import com.facebook.react.bridge.CatalystInstance;
44 53
 import com.facebook.react.bridge.LifecycleEventListener;
45 54
 import com.facebook.react.bridge.ReactContext;
46 55
 import com.facebook.react.bridge.ReadableArray;
47 56
 import com.facebook.react.bridge.ReadableMap;
48 57
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
49 58
 import com.facebook.react.bridge.WritableMap;
59
+import com.facebook.react.bridge.WritableNativeArray;
60
+import com.facebook.react.bridge.WritableNativeMap;
50 61
 import com.facebook.react.common.MapBuilder;
51 62
 import com.facebook.react.common.build.ReactBuildConfig;
52 63
 import com.facebook.react.module.annotations.ReactModule;
@@ -57,6 +68,7 @@ import com.facebook.react.uimanager.annotations.ReactProp;
57 68
 import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
58 69
 import com.facebook.react.uimanager.events.Event;
59 70
 import com.facebook.react.uimanager.events.EventDispatcher;
71
+import com.reactnativecommunity.webview.RNCWebViewModule.ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState;
60 72
 import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
61 73
 import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
62 74
 import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
@@ -64,6 +76,7 @@ import com.reactnativecommunity.webview.events.TopLoadingProgressEvent;
64 76
 import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
65 77
 import com.reactnativecommunity.webview.events.TopMessageEvent;
66 78
 import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
79
+import com.reactnativecommunity.webview.events.TopRenderProcessGoneEvent;
67 80
 
68 81
 import org.json.JSONException;
69 82
 import org.json.JSONObject;
@@ -76,8 +89,7 @@ import java.util.ArrayList;
76 89
 import java.util.HashMap;
77 90
 import java.util.Locale;
78 91
 import java.util.Map;
79
-
80
-import javax.annotation.Nullable;
92
+import java.util.concurrent.atomic.AtomicReference;
81 93
 
82 94
 /**
83 95
  * Manages instances of {@link WebView}
@@ -105,8 +117,8 @@ import javax.annotation.Nullable;
105 117
  */
106 118
 @ReactModule(name = RNCWebViewManager.REACT_CLASS)
107 119
 public class RNCWebViewManager extends SimpleViewManager<WebView> {
120
+  private static final String TAG = "RNCWebViewManager";
108 121
 
109
-  public static String activeUrl = null;
110 122
   public static final int COMMAND_GO_BACK = 1;
111 123
   public static final int COMMAND_GO_FORWARD = 2;
112 124
   public static final int COMMAND_RELOAD = 3;
@@ -129,6 +141,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
129 141
   // Use `webView.loadUrl("about:blank")` to reliably reset the view
130 142
   // state and release page resources (including any running JavaScript).
131 143
   protected static final String BLANK_URL = "about:blank";
144
+  protected static final int SHOULD_OVERRIDE_URL_LOADING_TIMEOUT = 250;
132 145
   protected WebViewConfig mWebViewConfig;
133 146
 
134 147
   protected RNCWebChromeClient mWebChromeClient = null;
@@ -194,6 +207,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
194 207
 
195 208
     webView.setDownloadListener(new DownloadListener() {
196 209
       public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
210
+        webView.setIgnoreErrFailedForThisURL(url);
211
+
197 212
         RNCWebViewModule module = getModule(reactContext);
198 213
 
199 214
         DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
@@ -208,7 +223,6 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
208 223
           String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
209 224
           String cookie = CookieManager.getInstance().getCookie(baseUrl);
210 225
           request.addRequestHeader("Cookie", cookie);
211
-          System.out.println("Got cookie for DownloadManager: " + cookie);
212 226
         } catch (MalformedURLException e) {
213 227
           System.out.println("Error getting cookie for DownloadManager: " + e.toString());
214 228
           e.printStackTrace();
@@ -291,6 +305,21 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
291 305
     }
292 306
   }
293 307
 
308
+  @ReactProp(name = "androidLayerType")
309
+  public void setLayerType(WebView view, String layerTypeString) {
310
+    int layerType = View.LAYER_TYPE_NONE;
311
+    switch (layerTypeString) {
312
+        case "hardware":
313
+          layerType = View.LAYER_TYPE_HARDWARE;
314
+          break;
315
+        case "software":
316
+          layerType = View.LAYER_TYPE_SOFTWARE;
317
+          break;
318
+    }
319
+    view.setLayerType(layerType, null);
320
+  }
321
+
322
+
294 323
   @ReactProp(name = "overScrollMode")
295 324
   public void setOverScrollMode(WebView view, String overScrollModeString) {
296 325
     Integer overScrollMode;
@@ -372,6 +401,11 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
372 401
     view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
373 402
   }
374 403
 
404
+  @ReactProp(name = "javaScriptCanOpenWindowsAutomatically")
405
+  public void setJavaScriptCanOpenWindowsAutomatically(WebView view, boolean enabled) {
406
+    view.getSettings().setJavaScriptCanOpenWindowsAutomatically(enabled);
407
+  }
408
+
375 409
   @ReactProp(name = "allowFileAccessFromFileURLs")
376 410
   public void setAllowFileAccessFromFileURLs(WebView view, boolean allow) {
377 411
     view.getSettings().setAllowFileAccessFromFileURLs(allow);
@@ -392,13 +426,38 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
392 426
     ((RNCWebView) view).setInjectedJavaScript(injectedJavaScript);
393 427
   }
394 428
 
429
+  @ReactProp(name = "injectedJavaScriptBeforeContentLoaded")
430
+  public void setInjectedJavaScriptBeforeContentLoaded(WebView view, @Nullable String injectedJavaScriptBeforeContentLoaded) {
431
+    ((RNCWebView) view).setInjectedJavaScriptBeforeContentLoaded(injectedJavaScriptBeforeContentLoaded);
432
+  }
433
+
434
+  @ReactProp(name = "injectedJavaScriptForMainFrameOnly")
435
+  public void setInjectedJavaScriptForMainFrameOnly(WebView view, boolean enabled) {
436
+    ((RNCWebView) view).setInjectedJavaScriptForMainFrameOnly(enabled);
437
+  }
438
+
439
+  @ReactProp(name = "injectedJavaScriptBeforeContentLoadedForMainFrameOnly")
440
+  public void setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(WebView view, boolean enabled) {
441
+    ((RNCWebView) view).setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(enabled);
442
+  }
443
+
395 444
   @ReactProp(name = "messagingEnabled")
396 445
   public void setMessagingEnabled(WebView view, boolean enabled) {
397 446
     ((RNCWebView) view).setMessagingEnabled(enabled);
398 447
   }
399
-   
448
+
449
+  @ReactProp(name = "messagingModuleName")
450
+  public void setMessagingModuleName(WebView view, String moduleName) {
451
+    ((RNCWebView) view).setMessagingModuleName(moduleName);
452
+  }
453
+
400 454
   @ReactProp(name = "incognito")
401 455
   public void setIncognito(WebView view, boolean enabled) {
456
+    // Don't do anything when incognito is disabled
457
+    if (!enabled) {
458
+      return;
459
+    }
460
+
402 461
     // Remove all previous cookies
403 462
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
404 463
       CookieManager.getInstance().removeAllCookies(null);
@@ -408,14 +467,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
408 467
 
409 468
     // Disable caching
410 469
     view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
411
-    view.getSettings().setAppCacheEnabled(!enabled);
470
+    view.getSettings().setAppCacheEnabled(false);
412 471
     view.clearHistory();
413
-    view.clearCache(enabled);
472
+    view.clearCache(true);
414 473
 
415 474
     // No form data or autofill enabled
416 475
     view.clearFormData();
417
-    view.getSettings().setSavePassword(!enabled);
418
-    view.getSettings().setSaveFormData(!enabled);
476
+    view.getSettings().setSavePassword(false);
477
+    view.getSettings().setSaveFormData(false);
419 478
   }
420 479
 
421 480
   @ReactProp(name = "source")
@@ -545,6 +604,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
545 604
     export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
546 605
     export.put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll"));
547 606
     export.put(TopHttpErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onHttpError"));
607
+    export.put(TopRenderProcessGoneEvent.EVENT_NAME, MapBuilder.of("registrationName", "onRenderProcessGone"));
548 608
     return export;
549 609
   }
550 610
 
@@ -609,6 +669,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
609 669
         if (args == null) {
610 670
           throw new RuntimeException("Arguments for loading an url are null!");
611 671
         }
672
+        ((RNCWebView) root).progressChangedFilter.setWaitingForCommandLoadUrl(false);
612 673
         root.loadUrl(args.getString(0));
613 674
         break;
614 675
       case COMMAND_FOCUS:
@@ -646,7 +707,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
646 707
         public Bitmap getDefaultVideoPoster() {
647 708
           return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
648 709
         }
649
-        
710
+
650 711
         @Override
651 712
         public void onShowCustomView(View view, CustomViewCallback callback) {
652 713
           if (mVideoView != null) {
@@ -714,6 +775,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
714 775
     protected boolean mLastLoadFailed = false;
715 776
     protected @Nullable
716 777
     ReadableArray mUrlPrefixesForDefaultIntent;
778
+    protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
779
+    protected @Nullable String ignoreErrFailedForThisURL = null;
780
+
781
+    public void setIgnoreErrFailedForThisURL(@Nullable String url) {
782
+      ignoreErrFailedForThisURL = url;
783
+    }
717 784
 
718 785
     @Override
719 786
     public void onPageFinished(WebView webView, String url) {
@@ -733,6 +800,9 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
733 800
       super.onPageStarted(webView, url, favicon);
734 801
       mLastLoadFailed = false;
735 802
 
803
+      RNCWebView reactWebView = (RNCWebView) webView;
804
+      reactWebView.callInjectedJavaScriptBeforeContentLoaded();
805
+
736 806
       dispatchEvent(
737 807
         webView,
738 808
         new TopLoadingStartEvent(
@@ -742,15 +812,52 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
742 812
 
743 813
     @Override
744 814
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
745
-      activeUrl = url;
746
-      dispatchEvent(
747
-        view,
748
-        new TopShouldStartLoadWithRequestEvent(
749
-          view.getId(),
750
-          createWebViewEvent(view, url)));
751
-      return true;
752
-    }
815
+      final RNCWebView rncWebView = (RNCWebView) view;
816
+      final boolean isJsDebugging = ((ReactContext) view.getContext()).getJavaScriptContextHolder().get() == 0;
817
+
818
+      if (!isJsDebugging && rncWebView.mCatalystInstance != null) {
819
+        final Pair<Integer, AtomicReference<ShouldOverrideCallbackState>> lock = RNCWebViewModule.shouldOverrideUrlLoadingLock.getNewLock();
820
+        final int lockIdentifier = lock.first;
821
+        final AtomicReference<ShouldOverrideCallbackState> lockObject = lock.second;
753 822
 
823
+        final WritableMap event = createWebViewEvent(view, url);
824
+        event.putInt("lockIdentifier", lockIdentifier);
825
+        rncWebView.sendDirectMessage("onShouldStartLoadWithRequest", event);
826
+
827
+        try {
828
+          assert lockObject != null;
829
+          synchronized (lockObject) {
830
+            final long startTime = SystemClock.elapsedRealtime();
831
+            while (lockObject.get() == ShouldOverrideCallbackState.UNDECIDED) {
832
+              if (SystemClock.elapsedRealtime() - startTime > SHOULD_OVERRIDE_URL_LOADING_TIMEOUT) {
833
+                FLog.w(TAG, "Did not receive response to shouldOverrideUrlLoading in time, defaulting to allow loading.");
834
+                RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
835
+                return false;
836
+              }
837
+              lockObject.wait(SHOULD_OVERRIDE_URL_LOADING_TIMEOUT);
838
+            }
839
+          }
840
+        } catch (InterruptedException e) {
841
+          FLog.e(TAG, "shouldOverrideUrlLoading was interrupted while waiting for result.", e);
842
+          RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
843
+          return false;
844
+        }
845
+
846
+        final boolean shouldOverride = lockObject.get() == ShouldOverrideCallbackState.SHOULD_OVERRIDE;
847
+        RNCWebViewModule.shouldOverrideUrlLoadingLock.removeLock(lockIdentifier);
848
+
849
+        return shouldOverride;
850
+      } else {
851
+        FLog.w(TAG, "Couldn't use blocking synchronous call for onShouldStartLoadWithRequest due to debugging or missing Catalyst instance, falling back to old event-and-load.");
852
+        progressChangedFilter.setWaitingForCommandLoadUrl(true);
853
+        dispatchEvent(
854
+          view,
855
+          new TopShouldStartLoadWithRequestEvent(
856
+            view.getId(),
857
+            createWebViewEvent(view, url)));
858
+        return true;
859
+      }
860
+    }
754 861
 
755 862
     @TargetApi(Build.VERSION_CODES.N)
756 863
     @Override
@@ -759,12 +866,71 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
759 866
       return this.shouldOverrideUrlLoading(view, url);
760 867
     }
761 868
 
869
+    @Override
870
+    public void onReceivedSslError(final WebView webView, final SslErrorHandler handler, final SslError error) {
871
+        handler.cancel();
872
+
873
+        int code = error.getPrimaryError();
874
+        String failingUrl = error.getUrl();
875
+        String description = "";
876
+        String descriptionPrefix = "SSL error: ";
877
+
878
+        // https://developer.android.com/reference/android/net/http/SslError.html
879
+        switch (code) {
880
+          case SslError.SSL_DATE_INVALID:
881
+            description = "The date of the certificate is invalid";
882
+            break;
883
+          case SslError.SSL_EXPIRED:
884
+            description = "The certificate has expired";
885
+            break;
886
+          case SslError.SSL_IDMISMATCH:
887
+            description = "Hostname mismatch";
888
+            break;
889
+          case SslError.SSL_INVALID:
890
+            description = "A generic error occurred";
891
+            break;
892
+          case SslError.SSL_NOTYETVALID:
893
+            description = "The certificate is not yet valid";
894
+            break;
895
+          case SslError.SSL_UNTRUSTED:
896
+            description = "The certificate authority is not trusted";
897
+            break;
898
+          default:
899
+            description = "Unknown SSL Error";
900
+            break;
901
+        }
902
+
903
+        description = descriptionPrefix + description;
904
+
905
+        this.onReceivedError(
906
+          webView,
907
+          code,
908
+          description,
909
+          failingUrl
910
+        );
911
+    }
912
+
762 913
     @Override
763 914
     public void onReceivedError(
764 915
       WebView webView,
765 916
       int errorCode,
766 917
       String description,
767 918
       String failingUrl) {
919
+
920
+      if (ignoreErrFailedForThisURL != null
921
+          && failingUrl.equals(ignoreErrFailedForThisURL)
922
+          && errorCode == -1
923
+          && description.equals("net::ERR_FAILED")) {
924
+
925
+        // This is a workaround for a bug in the WebView.
926
+        // See these chromium issues for more context:
927
+        // https://bugs.chromium.org/p/chromium/issues/detail?id=1023678
928
+        // https://bugs.chromium.org/p/chromium/issues/detail?id=1050635
929
+        // This entire commit should be reverted once this bug is resolved in chromium.
930
+        setIgnoreErrFailedForThisURL(null);
931
+        return;
932
+      }
933
+
768 934
       super.onReceivedError(webView, errorCode, description, failingUrl);
769 935
       mLastLoadFailed = true;
770 936
 
@@ -800,6 +966,41 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
800 966
       }
801 967
     }
802 968
 
969
+    @TargetApi(Build.VERSION_CODES.O)
970
+    @Override
971
+    public boolean onRenderProcessGone(WebView webView, RenderProcessGoneDetail detail) {
972
+        // WebViewClient.onRenderProcessGone was added in O.
973
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
974
+            return false;
975
+        }
976
+        super.onRenderProcessGone(webView, detail);
977
+
978
+        if(detail.didCrash()){
979
+          Log.e("RNCWebViewManager", "The WebView rendering process crashed.");
980
+        }
981
+        else{
982
+          Log.w("RNCWebViewManager", "The WebView rendering process was killed by the system.");
983
+        }
984
+
985
+        // if webView is null, we cannot return any event
986
+        // since the view is already dead/disposed
987
+        // still prevent the app crash by returning true.
988
+        if(webView == null){
989
+          return true;
990
+        }
991
+
992
+        WritableMap event = createWebViewEvent(webView, webView.getUrl());
993
+        event.putBoolean("didCrash", detail.didCrash());
994
+
995
+        dispatchEvent(
996
+          webView,
997
+          new TopRenderProcessGoneEvent(webView.getId(), event)
998
+        );
999
+
1000
+        // returning false would crash the app.
1001
+        return true;
1002
+    }
1003
+
803 1004
     protected void emitFinishEvent(WebView webView, String url) {
804 1005
       dispatchEvent(
805 1006
         webView,
@@ -824,6 +1025,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
824 1025
     public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
825 1026
       mUrlPrefixesForDefaultIntent = specialUrls;
826 1027
     }
1028
+
1029
+    public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
1030
+      progressChangedFilter = filter;
1031
+    }
827 1032
   }
828 1033
 
829 1034
   protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
@@ -845,6 +1050,8 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
845 1050
     protected View mVideoView;
846 1051
     protected WebChromeClient.CustomViewCallback mCustomViewCallback;
847 1052
 
1053
+    protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
1054
+
848 1055
     public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
849 1056
       this.mReactContext = reactContext;
850 1057
       this.mWebView = webView;
@@ -899,11 +1106,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
899 1106
     public void onProgressChanged(WebView webView, int newProgress) {
900 1107
       super.onProgressChanged(webView, newProgress);
901 1108
       final String url = webView.getUrl();
902
-      if (
903
-        url != null
904
-        && activeUrl != null
905
-        && !url.equals(activeUrl)
906
-      ) {
1109
+      if (progressChangedFilter.isWaitingForCommandLoadUrl()) {
907 1110
         return;
908 1111
       }
909 1112
       WritableMap event = Arguments.createMap();
@@ -942,8 +1145,7 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
942 1145
     public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
943 1146
       String[] acceptTypes = fileChooserParams.getAcceptTypes();
944 1147
       boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
945
-      Intent intent = fileChooserParams.createIntent();
946
-      return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, intent, acceptTypes, allowMultiple);
1148
+      return getModule(mReactContext).startPhotoPickerIntent(filePathCallback, acceptTypes, allowMultiple);
947 1149
     }
948 1150
 
949 1151
     @Override
@@ -962,6 +1164,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
962 1164
     protected ViewGroup getRootView() {
963 1165
       return (ViewGroup) mReactContext.getCurrentActivity().findViewById(android.R.id.content);
964 1166
     }
1167
+
1168
+    public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
1169
+      progressChangedFilter = filter;
1170
+    }
965 1171
   }
966 1172
 
967 1173
   /**
@@ -971,12 +1177,27 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
971 1177
   protected static class RNCWebView extends WebView implements LifecycleEventListener {
972 1178
     protected @Nullable
973 1179
     String injectedJS;
1180
+    protected @Nullable
1181
+    String injectedJSBeforeContentLoaded;
1182
+
1183
+    /**
1184
+     * android.webkit.WebChromeClient fundamentally does not support JS injection into frames other
1185
+     * than the main frame, so these two properties are mostly here just for parity with iOS & macOS.
1186
+     */
1187
+    protected boolean injectedJavaScriptForMainFrameOnly = true;
1188
+    protected boolean injectedJavaScriptBeforeContentLoadedForMainFrameOnly = true;
1189
+
974 1190
     protected boolean messagingEnabled = false;
975 1191
     protected @Nullable
1192
+    String messagingModuleName;
1193
+    protected @Nullable
976 1194
     RNCWebViewClient mRNCWebViewClient;
1195
+    protected @Nullable
1196
+    CatalystInstance mCatalystInstance;
977 1197
     protected boolean sendContentSizeChangeEvents = false;
978 1198
     private OnScrollDispatchHelper mOnScrollDispatchHelper;
979 1199
     protected boolean hasScrollEvent = false;
1200
+    protected ProgressChangedFilter progressChangedFilter;
980 1201
 
981 1202
     /**
982 1203
      * WebView must be created with an context of the current activity
@@ -986,6 +1207,12 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
986 1207
      */
987 1208
     public RNCWebView(ThemedReactContext reactContext) {
988 1209
       super(reactContext);
1210
+      this.createCatalystInstance();
1211
+      progressChangedFilter = new ProgressChangedFilter();
1212
+    }
1213
+
1214
+    public void setIgnoreErrFailedForThisURL(String url) {
1215
+      mRNCWebViewClient.setIgnoreErrFailedForThisURL(url);
989 1216
     }
990 1217
 
991 1218
     public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
@@ -1032,6 +1259,17 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1032 1259
       super.setWebViewClient(client);
1033 1260
       if (client instanceof RNCWebViewClient) {
1034 1261
         mRNCWebViewClient = (RNCWebViewClient) client;
1262
+        mRNCWebViewClient.setProgressChangedFilter(progressChangedFilter);
1263
+      }
1264
+    }
1265
+
1266
+    WebChromeClient mWebChromeClient;
1267
+    @Override
1268
+    public void setWebChromeClient(WebChromeClient client) {
1269
+      this.mWebChromeClient = client;
1270
+      super.setWebChromeClient(client);
1271
+      if (client instanceof RNCWebChromeClient) {
1272
+        ((RNCWebChromeClient) client).setProgressChangedFilter(progressChangedFilter);
1035 1273
       }
1036 1274
     }
1037 1275
 
@@ -1044,10 +1282,30 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1044 1282
       injectedJS = js;
1045 1283
     }
1046 1284
 
1285
+    public void setInjectedJavaScriptBeforeContentLoaded(@Nullable String js) {
1286
+      injectedJSBeforeContentLoaded = js;
1287
+    }
1288
+
1289
+    public void setInjectedJavaScriptForMainFrameOnly(boolean enabled) {
1290
+      injectedJavaScriptForMainFrameOnly = enabled;
1291
+    }
1292
+
1293
+    public void setInjectedJavaScriptBeforeContentLoadedForMainFrameOnly(boolean enabled) {
1294
+      injectedJavaScriptBeforeContentLoadedForMainFrameOnly = enabled;
1295
+    }
1296
+
1047 1297
     protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1048 1298
       return new RNCWebViewBridge(webView);
1049 1299
     }
1050 1300
 
1301
+    protected void createCatalystInstance() {
1302
+      ReactContext reactContext = (ReactContext) this.getContext();
1303
+
1304
+      if (reactContext != null) {
1305
+        mCatalystInstance = reactContext.getCatalystInstance();
1306
+      }
1307
+    }
1308
+
1051 1309
     @SuppressLint("AddJavascriptInterface")
1052 1310
     public void setMessagingEnabled(boolean enabled) {
1053 1311
       if (messagingEnabled == enabled) {
@@ -1063,6 +1321,10 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1063 1321
       }
1064 1322
     }
1065 1323
 
1324
+    public void setMessagingModuleName(String moduleName) {
1325
+      messagingModuleName = moduleName;
1326
+    }
1327
+
1066 1328
     protected void evaluateJavascriptWithFallback(String script) {
1067 1329
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
1068 1330
         evaluateJavascript(script, null);
@@ -1085,7 +1347,18 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1085 1347
       }
1086 1348
     }
1087 1349
 
1350
+    public void callInjectedJavaScriptBeforeContentLoaded() {
1351
+      if (getSettings().getJavaScriptEnabled() &&
1352
+      injectedJSBeforeContentLoaded != null &&
1353
+      !TextUtils.isEmpty(injectedJSBeforeContentLoaded)) {
1354
+        evaluateJavascriptWithFallback("(function() {\n" + injectedJSBeforeContentLoaded + ";\n})();");
1355
+      }
1356
+    }
1357
+
1088 1358
     public void onMessage(String message) {
1359
+      ReactContext reactContext = (ReactContext) this.getContext();
1360
+      RNCWebView mContext = this;
1361
+
1089 1362
       if (mRNCWebViewClient != null) {
1090 1363
         WebView webView = this;
1091 1364
         webView.post(new Runnable() {
@@ -1096,16 +1369,36 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1096 1369
             }
1097 1370
             WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
1098 1371
             data.putString("data", message);
1099
-            dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
1372
+
1373
+            if (mCatalystInstance != null) {
1374
+              mContext.sendDirectMessage("onMessage", data);
1375
+            } else {
1376
+              dispatchEvent(webView, new TopMessageEvent(webView.getId(), data));
1377
+            }
1100 1378
           }
1101 1379
         });
1102 1380
       } else {
1103 1381
         WritableMap eventData = Arguments.createMap();
1104 1382
         eventData.putString("data", message);
1105
-        dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
1383
+
1384
+        if (mCatalystInstance != null) {
1385
+          this.sendDirectMessage("onMessage", eventData);
1386
+        } else {
1387
+          dispatchEvent(this, new TopMessageEvent(this.getId(), eventData));
1388
+        }
1106 1389
       }
1107 1390
     }
1108 1391
 
1392
+    protected void sendDirectMessage(final String method, WritableMap data) {
1393
+      WritableNativeMap event = new WritableNativeMap();
1394
+      event.putMap("nativeEvent", data);
1395
+
1396
+      WritableNativeArray params = new WritableNativeArray();
1397
+      params.pushMap(event);
1398
+
1399
+      mCatalystInstance.callFunction(messagingModuleName, method, params);
1400
+    }
1401
+
1109 1402
     protected void onScrollChanged(int x, int y, int oldX, int oldY) {
1110 1403
       super.onScrollChanged(x, y, oldX, oldY);
1111 1404
 
@@ -1139,6 +1432,14 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1139 1432
       destroy();
1140 1433
     }
1141 1434
 
1435
+    @Override
1436
+    public void destroy() {
1437
+      if (mWebChromeClient != null) {
1438
+        mWebChromeClient.onHideCustomView();
1439
+      }
1440
+      super.destroy();
1441
+    }
1442
+
1142 1443
     protected class RNCWebViewBridge {
1143 1444
       RNCWebView mContext;
1144 1445
 
@@ -1155,5 +1456,17 @@ public class RNCWebViewManager extends SimpleViewManager<WebView> {
1155 1456
         mContext.onMessage(message);
1156 1457
       }
1157 1458
     }
1459
+
1460
+    protected static class ProgressChangedFilter {
1461
+      private boolean waitingForCommandLoadUrl = false;
1462
+
1463
+      public void setWaitingForCommandLoadUrl(boolean isWaiting) {
1464
+        waitingForCommandLoadUrl = isWaiting;
1465
+      }
1466
+
1467
+      public boolean isWaitingForCommandLoadUrl() {
1468
+        return waitingForCommandLoadUrl;
1469
+      }
1470
+    }
1158 1471
   }
1159 1472
 }

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

@@ -11,9 +11,13 @@ import android.os.Build;
11 11
 import android.os.Environment;
12 12
 import android.os.Parcelable;
13 13
 import android.provider.MediaStore;
14
+
15
+import androidx.annotation.Nullable;
14 16
 import androidx.annotation.RequiresApi;
15 17
 import androidx.core.content.ContextCompat;
16 18
 import androidx.core.content.FileProvider;
19
+import androidx.core.util.Pair;
20
+
17 21
 import android.util.Log;
18 22
 import android.webkit.MimeTypeMap;
19 23
 import android.webkit.ValueCallback;
@@ -32,6 +36,9 @@ import com.facebook.react.modules.core.PermissionListener;
32 36
 import java.io.File;
33 37
 import java.io.IOException;
34 38
 import java.util.ArrayList;
39
+import java.util.Arrays;
40
+import java.util.HashMap;
41
+import java.util.concurrent.atomic.AtomicReference;
35 42
 
36 43
 import static android.app.Activity.RESULT_OK;
37 44
 
@@ -41,11 +48,53 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
41 48
   private static final int PICKER = 1;
42 49
   private static final int PICKER_LEGACY = 3;
43 50
   private static final int FILE_DOWNLOAD_PERMISSION_REQUEST = 1;
44
-  final String DEFAULT_MIME_TYPES = "*/*";
45 51
   private ValueCallback<Uri> filePathCallbackLegacy;
46 52
   private ValueCallback<Uri[]> filePathCallback;
47
-  private Uri outputFileUri;
53
+  private File outputImage;
54
+  private File outputVideo;
48 55
   private DownloadManager.Request downloadRequest;
56
+
57
+  protected static class ShouldOverrideUrlLoadingLock {
58
+    protected enum ShouldOverrideCallbackState {
59
+      UNDECIDED,
60
+      SHOULD_OVERRIDE,
61
+      DO_NOT_OVERRIDE,
62
+    }
63
+
64
+    private int nextLockIdentifier = 0;
65
+    private final HashMap<Integer, AtomicReference<ShouldOverrideCallbackState>> shouldOverrideLocks = new HashMap<>();
66
+
67
+    public synchronized Pair<Integer, AtomicReference<ShouldOverrideCallbackState>> getNewLock() {
68
+      final int lockIdentifier = nextLockIdentifier++;
69
+      final AtomicReference<ShouldOverrideCallbackState> shouldOverride = new AtomicReference<>(ShouldOverrideCallbackState.UNDECIDED);
70
+      shouldOverrideLocks.put(lockIdentifier, shouldOverride);
71
+      return new Pair<>(lockIdentifier, shouldOverride);
72
+    }
73
+
74
+    @Nullable
75
+    public synchronized AtomicReference<ShouldOverrideCallbackState> getLock(Integer lockIdentifier) {
76
+      return shouldOverrideLocks.get(lockIdentifier);
77
+    }
78
+
79
+    public synchronized void removeLock(Integer lockIdentifier) {
80
+      shouldOverrideLocks.remove(lockIdentifier);
81
+    }
82
+  }
83
+
84
+  protected static final ShouldOverrideUrlLoadingLock shouldOverrideUrlLoadingLock = new ShouldOverrideUrlLoadingLock();
85
+
86
+  private enum MimeType {
87
+    DEFAULT("*/*"),
88
+    IMAGE("image"),
89
+    VIDEO("video");
90
+
91
+    private final String value;
92
+
93
+    MimeType(String value) {
94
+      this.value = value;
95
+    }
96
+  }
97
+
49 98
   private PermissionListener webviewFileDownloaderPermissionListener = new PermissionListener() {
50 99
     @Override
51 100
     public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
@@ -89,12 +138,33 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
89 138
     promise.resolve(result);
90 139
   }
91 140
 
141
+  @ReactMethod(isBlockingSynchronousMethod = true)
142
+  public void onShouldStartLoadWithRequestCallback(final boolean shouldStart, final int lockIdentifier) {
143
+    final AtomicReference<ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState> lockObject = shouldOverrideUrlLoadingLock.getLock(lockIdentifier);
144
+    if (lockObject != null) {
145
+      synchronized (lockObject) {
146
+        lockObject.set(shouldStart ? ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState.DO_NOT_OVERRIDE : ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState.SHOULD_OVERRIDE);
147
+        lockObject.notify();
148
+      }
149
+    }
150
+  }
151
+
92 152
   public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
93 153
 
94 154
     if (filePathCallback == null && filePathCallbackLegacy == null) {
95 155
       return;
96 156
     }
97 157
 
158
+    boolean imageTaken = false;
159
+    boolean videoTaken = false;
160
+
161
+    if (outputImage != null && outputImage.length() > 0) {
162
+      imageTaken = true;
163
+    }
164
+    if (outputVideo != null && outputVideo.length() > 0) {
165
+      videoTaken = true;
166
+    }
167
+
98 168
     // based off of which button was pressed, we get an activity result and a file
99 169
     // the camera activity doesn't properly return the filename* (I think?) so we use
100 170
     // this filename instead
@@ -105,23 +175,42 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
105 175
             filePathCallback.onReceiveValue(null);
106 176
           }
107 177
         } else {
108
-          Uri result[] = this.getSelectedFiles(data, resultCode);
109
-          if (result != null) {
110
-            filePathCallback.onReceiveValue(result);
178
+          if (imageTaken) {
179
+            filePathCallback.onReceiveValue(new Uri[]{getOutputUri(outputImage)});
180
+          } else if (videoTaken) {
181
+            filePathCallback.onReceiveValue(new Uri[]{getOutputUri(outputVideo)});
111 182
           } else {
112
-            filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
183
+            filePathCallback.onReceiveValue(this.getSelectedFiles(data, resultCode));
113 184
           }
114 185
         }
115 186
         break;
116 187
       case PICKER_LEGACY:
117
-        Uri result = resultCode != Activity.RESULT_OK ? null : data == null ? outputFileUri : data.getData();
118
-        filePathCallbackLegacy.onReceiveValue(result);
188
+        if (resultCode != RESULT_OK) {
189
+          filePathCallbackLegacy.onReceiveValue(null);
190
+        } else {
191
+          if (imageTaken) {
192
+            filePathCallbackLegacy.onReceiveValue(getOutputUri(outputImage));
193
+          } else if (videoTaken) {
194
+            filePathCallbackLegacy.onReceiveValue(getOutputUri(outputVideo));
195
+          } else {
196
+            filePathCallbackLegacy.onReceiveValue(data.getData());
197
+          }
198
+        }
119 199
         break;
120 200
 
121 201
     }
202
+
203
+    if (outputImage != null && !imageTaken) {
204
+      outputImage.delete();
205
+    }
206
+    if (outputVideo != null && !videoTaken) {
207
+      outputVideo.delete();
208
+    }
209
+
122 210
     filePathCallback = null;
123 211
     filePathCallbackLegacy = null;
124
-    outputFileUri = null;
212
+    outputImage = null;
213
+    outputVideo = null;
125 214
   }
126 215
 
127 216
   public void onNewIntent(Intent intent) {
@@ -132,15 +221,6 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
132 221
       return null;
133 222
     }
134 223
 
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 224
     // we have multiple files selected
145 225
     if (data.getClipData() != null) {
146 226
       final int numSelectedFiles = data.getClipData().getItemCount();
@@ -150,6 +230,12 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
150 230
       }
151 231
       return result;
152 232
     }
233
+
234
+    // we have one file selected
235
+    if (data.getData() != null && resultCode == RESULT_OK && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
236
+      return WebChromeClient.FileChooserParams.parseResult(resultCode, data);
237
+    }
238
+
153 239
     return null;
154 240
   }
155 241
 
@@ -161,10 +247,16 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
161 247
 
162 248
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
163 249
     if (acceptsImages(acceptType)) {
164
-      extraIntents.add(getPhotoIntent());
250
+      Intent photoIntent = getPhotoIntent();
251
+      if (photoIntent != null) {
252
+        extraIntents.add(photoIntent);
253
+      }
165 254
     }
166 255
     if (acceptsVideo(acceptType)) {
167
-      extraIntents.add(getVideoIntent());
256
+      Intent videoIntent = getVideoIntent();
257
+      if (videoIntent != null) {
258
+        extraIntents.add(videoIntent);
259
+      }
168 260
     }
169 261
     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
170 262
 
@@ -176,15 +268,23 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
176 268
   }
177 269
 
178 270
   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
179
-  public boolean startPhotoPickerIntent(final ValueCallback<Uri[]> callback, final Intent intent, final String[] acceptTypes, final boolean allowMultiple) {
271
+  public boolean startPhotoPickerIntent(final ValueCallback<Uri[]> callback, final String[] acceptTypes, final boolean allowMultiple) {
180 272
     filePathCallback = callback;
181 273
 
182 274
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
183
-    if (acceptsImages(acceptTypes)) {
184
-      extraIntents.add(getPhotoIntent());
185
-    }
186
-    if (acceptsVideo(acceptTypes)) {
187
-      extraIntents.add(getVideoIntent());
275
+    if (!needsCameraPermission()) {
276
+      if (acceptsImages(acceptTypes)) {
277
+        Intent photoIntent = getPhotoIntent();
278
+        if (photoIntent != null) {
279
+          extraIntents.add(photoIntent);
280
+        }
281
+      }
282
+      if (acceptsVideo(acceptTypes)) {
283
+        Intent videoIntent = getVideoIntent();
284
+        if (videoIntent != null) {
285
+          extraIntents.add(videoIntent);
286
+        }
287
+      }
188 288
     }
189 289
 
190 290
     Intent fileSelectionIntent = getFileChooserIntent(acceptTypes, allowMultiple);
@@ -216,16 +316,13 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
216 316
   }
217 317
 
218 318
   public boolean grantFileDownloaderPermissions() {
219
-    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
319
+    // Permission not required for Android Q and above
320
+    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
220 321
       return true;
221 322
     }
222 323
 
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) {
324
+    boolean result = ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
325
+    if (!result && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
229 326
       PermissionAwareActivity activity = getPermissionAwareActivity();
230 327
       activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, FILE_DOWNLOAD_PERMISSION_REQUEST, webviewFileDownloaderPermissionListener);
231 328
     }
@@ -233,24 +330,59 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
233 330
     return result;
234 331
   }
235 332
 
333
+  protected boolean needsCameraPermission() {
334
+    boolean needed = false;
335
+
336
+    PackageManager packageManager = getCurrentActivity().getPackageManager();
337
+    try {
338
+      String[] requestedPermissions = packageManager.getPackageInfo(getReactApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS).requestedPermissions;
339
+      if (Arrays.asList(requestedPermissions).contains(Manifest.permission.CAMERA)
340
+        && ContextCompat.checkSelfPermission(getCurrentActivity(), Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
341
+        needed = true;
342
+      }
343
+    } catch (PackageManager.NameNotFoundException e) {
344
+      needed = true;
345
+    }
346
+
347
+    return needed;
348
+  }
349
+
236 350
   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);
351
+    Intent intent = null;
352
+
353
+    try {
354
+      outputImage = getCapturedFile(MimeType.IMAGE);
355
+      Uri outputImageUri = getOutputUri(outputImage);
356
+      intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
357
+      intent.putExtra(MediaStore.EXTRA_OUTPUT, outputImageUri);
358
+    } catch (IOException | IllegalArgumentException e) {
359
+      Log.e("CREATE FILE", "Error occurred while creating the File", e);
360
+      e.printStackTrace();
361
+    }
362
+
240 363
     return intent;
241 364
   }
242 365
 
243 366
   private Intent getVideoIntent() {
244
-    Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
245
-    outputFileUri = getOutputUri(MediaStore.ACTION_VIDEO_CAPTURE);
246
-    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
367
+    Intent intent = null;
368
+
369
+    try {
370
+      outputVideo = getCapturedFile(MimeType.VIDEO);
371
+      Uri outputVideoUri = getOutputUri(outputVideo);
372
+      intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
373
+      intent.putExtra(MediaStore.EXTRA_OUTPUT, outputVideoUri);
374
+    } catch (IOException | IllegalArgumentException e) {
375
+      Log.e("CREATE FILE", "Error occurred while creating the File", e);
376
+      e.printStackTrace();
377
+    }
378
+    
247 379
     return intent;
248 380
   }
249 381
 
250 382
   private Intent getFileChooserIntent(String acceptTypes) {
251 383
     String _acceptTypes = acceptTypes;
252 384
     if (acceptTypes.isEmpty()) {
253
-      _acceptTypes = DEFAULT_MIME_TYPES;
385
+      _acceptTypes = MimeType.DEFAULT.value;
254 386
     }
255 387
     if (acceptTypes.matches("\\.\\w+")) {
256 388
       _acceptTypes = getMimeTypeFromExtension(acceptTypes.replace(".", ""));
@@ -264,7 +396,7 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
264 396
   private Intent getFileChooserIntent(String[] acceptTypes, boolean allowMultiple) {
265 397
     Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
266 398
     intent.addCategory(Intent.CATEGORY_OPENABLE);
267
-    intent.setType("*/*");
399
+    intent.setType(MimeType.DEFAULT.value);
268 400
     intent.putExtra(Intent.EXTRA_MIME_TYPES, getAcceptedMimeType(acceptTypes));
269 401
     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
270 402
     return intent;
@@ -275,25 +407,33 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
275 407
     if (types.matches("\\.\\w+")) {
276 408
       mimeType = getMimeTypeFromExtension(types.replace(".", ""));
277 409
     }
278
-    return mimeType.isEmpty() || mimeType.toLowerCase().contains("image");
410
+    return mimeType.isEmpty() || mimeType.toLowerCase().contains(MimeType.IMAGE.value);
279 411
   }
280 412
 
281 413
   private Boolean acceptsImages(String[] types) {
282 414
     String[] mimeTypes = getAcceptedMimeType(types);
283
-    return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "image");
415
+    return arrayContainsString(mimeTypes, MimeType.DEFAULT.value) || arrayContainsString(mimeTypes, MimeType.IMAGE.value);
284 416
   }
285 417
 
286 418
   private Boolean acceptsVideo(String types) {
419
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
420
+      return false;
421
+    }
422
+
287 423
     String mimeType = types;
288 424
     if (types.matches("\\.\\w+")) {
289 425
       mimeType = getMimeTypeFromExtension(types.replace(".", ""));
290 426
     }
291
-    return mimeType.isEmpty() || mimeType.toLowerCase().contains("video");
427
+    return mimeType.isEmpty() || mimeType.toLowerCase().contains(MimeType.VIDEO.value);
292 428
   }
293 429
 
294 430
   private Boolean acceptsVideo(String[] types) {
431
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
432
+      return false;
433
+    }
434
+
295 435
     String[] mimeTypes = getAcceptedMimeType(types);
296
-    return isArrayEmpty(mimeTypes) || arrayContainsString(mimeTypes, "video");
436
+    return arrayContainsString(mimeTypes, MimeType.DEFAULT.value) || arrayContainsString(mimeTypes, MimeType.VIDEO.value);
297 437
   }
298 438
 
299 439
   private Boolean arrayContainsString(String[] array, String pattern) {
@@ -306,8 +446,8 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
306 446
   }
307 447
 
308 448
   private String[] getAcceptedMimeType(String[] types) {
309
-    if (isArrayEmpty(types)) {
310
-      return new String[]{DEFAULT_MIME_TYPES};
449
+    if (noAcceptTypesSet(types)) {
450
+      return new String[]{MimeType.DEFAULT.value};
311 451
     }
312 452
     String[] mimeTypes = new String[types.length];
313 453
     for (int i = 0; i < types.length; i++) {
@@ -315,7 +455,11 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
315 455
       // convert file extensions to mime types
316 456
       if (t.matches("\\.\\w+")) {
317 457
         String mimeType = getMimeTypeFromExtension(t.replace(".", ""));
318
-        mimeTypes[i] = mimeType;
458
+        if(mimeType != null) {
459
+          mimeTypes[i] = mimeType;
460
+        } else {
461
+          mimeTypes[i] = t;
462
+        }
319 463
       } else {
320 464
         mimeTypes[i] = t;
321 465
       }
@@ -331,15 +475,7 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
331 475
     return type;
332 476
   }
333 477
 
334
-  private Uri getOutputUri(String intentType) {
335
-    File capturedFile = null;
336
-    try {
337
-      capturedFile = getCapturedFile(intentType);
338
-    } catch (IOException e) {
339
-      Log.e("CREATE FILE", "Error occurred while creating the File", e);
340
-      e.printStackTrace();
341
-    }
342
-
478
+  private Uri getOutputUri(File capturedFile) {
343 479
     // for versions below 6.0 (23) we use the old File creation & permissions model
344 480
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
345 481
       return Uri.fromFile(capturedFile);
@@ -350,41 +486,50 @@ public class RNCWebViewModule extends ReactContextBaseJavaModule implements Acti
350 486
     return FileProvider.getUriForFile(getReactApplicationContext(), packageName + ".fileprovider", capturedFile);
351 487
   }
352 488
 
353
-  private File getCapturedFile(String intentType) throws IOException {
489
+  private File getCapturedFile(MimeType mimeType) throws IOException {
354 490
     String prefix = "";
355 491
     String suffix = "";
356 492
     String dir = "";
357
-    String filename = "";
358 493
 
359
-    if (intentType.equals(MediaStore.ACTION_IMAGE_CAPTURE)) {
360
-      prefix = "image-";
361
-      suffix = ".jpg";
362
-      dir = Environment.DIRECTORY_PICTURES;
363
-    } else if (intentType.equals(MediaStore.ACTION_VIDEO_CAPTURE)) {
364
-      prefix = "video-";
365
-      suffix = ".mp4";
366
-      dir = Environment.DIRECTORY_MOVIES;
494
+    switch (mimeType) {
495
+      case IMAGE:
496
+        prefix = "image-";
497
+        suffix = ".jpg";
498
+        dir = Environment.DIRECTORY_PICTURES;
499
+        break;
500
+      case VIDEO:
501
+        prefix = "video-";
502
+        suffix = ".mp4";
503
+        dir = Environment.DIRECTORY_MOVIES;
504
+        break;
505
+
506
+      default:
507
+        break;
367 508
     }
368 509
 
369
-    filename = prefix + String.valueOf(System.currentTimeMillis()) + suffix;
510
+    String filename = prefix + String.valueOf(System.currentTimeMillis()) + suffix;
511
+    File outputFile = null;
370 512
 
371 513
     // for versions below 6.0 (23) we use the old File creation & permissions model
372 514
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
373 515
       // only this Directory works on all tested Android versions
374 516
       // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
375 517
       File storageDir = Environment.getExternalStoragePublicDirectory(dir);
376
-      return new File(storageDir, filename);
518
+      outputFile = new File(storageDir, filename);
519
+    } else {
520
+      File storageDir = getReactApplicationContext().getExternalFilesDir(null);
521
+      outputFile = File.createTempFile(prefix, suffix, storageDir);
377 522
     }
378 523
 
379
-    File storageDir = getReactApplicationContext().getExternalFilesDir(null);
380
-    return File.createTempFile(filename, suffix, storageDir);
524
+    return outputFile;
381 525
   }
382 526
 
383
-  private Boolean isArrayEmpty(String[] arr) {
527
+  private Boolean noAcceptTypesSet(String[] types) {
384 528
     // when our array returned from getAcceptTypes() has no values set from the webview
385 529
     // i.e. <input type="file" />, without any "accept" attr
386 530
     // will be an array with one empty string element, afaik
387
-    return arr.length == 0 || (arr.length == 1 && arr[0].length() == 0);
531
+
532
+    return types.length == 0 || (types.length == 1 && types[0] != null && types[0].length() == 0);
388 533
   }
389 534
 
390 535
   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
+}

+ 26
- 0
android/src/main/java/com/reactnativecommunity/webview/events/TopRenderProcessGoneEvent.kt 查看文件

@@ -0,0 +1,26 @@
1
+package com.reactnativecommunity.webview.events
2
+
3
+import com.facebook.react.bridge.WritableMap
4
+import com.facebook.react.uimanager.events.Event
5
+import com.facebook.react.uimanager.events.RCTEventEmitter
6
+
7
+/**
8
+ * Event emitted when the WebView's process has crashed or
9
+   was killed by the OS.
10
+ */
11
+class TopRenderProcessGoneEvent(viewId: Int, private val mEventData: WritableMap) :
12
+  Event<TopRenderProcessGoneEvent>(viewId) {
13
+  companion object {
14
+    const val EVENT_NAME = "topRenderProcessGone"
15
+  }
16
+
17
+  override fun getEventName(): String = EVENT_NAME
18
+
19
+  override fun canCoalesce(): Boolean = false
20
+
21
+  override fun getCoalescingKey(): Short = 0
22
+
23
+  override fun dispatch(rctEventEmitter: RCTEventEmitter) =
24
+    rctEventEmitter.receiveEvent(viewTag, eventName, mEventData)
25
+
26
+}

+ 2
- 0
android/src/main/java/com/reactnativecommunity/webview/events/TopShouldStartLoadWithRequestEvent.kt 查看文件

@@ -14,6 +14,8 @@ class TopShouldStartLoadWithRequestEvent(viewId: Int, private val mData: Writabl
14 14
 
15 15
   init {
16 16
     mData.putString("navigationType", "other")
17
+    // Android does not raise shouldOverrideUrlLoading for inner frames
18
+    mData.putBoolean("isTopFrame", true)
17 19
   }
18 20
 
19 21
   override fun getEventName(): String = EVENT_NAME

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


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


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

@@ -19,6 +19,11 @@
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;
@@ -26,8 +31,11 @@
26 31
 @property (nonatomic, assign) BOOL messagingEnabled;
27 32
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScript;
28 33
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScriptBeforeContentLoaded;
34
+@property (nonatomic, assign) BOOL injectedJavaScriptForMainFrameOnly;
35
+@property (nonatomic, assign) BOOL injectedJavaScriptBeforeContentLoadedForMainFrameOnly;
29 36
 @property (nonatomic, assign) BOOL scrollEnabled;
30 37
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
38
+@property (nonatomic, assign) BOOL autoManageStatusBarEnabled;
31 39
 @property (nonatomic, assign) BOOL pagingEnabled;
32 40
 @property (nonatomic, assign) CGFloat decelerationRate;
33 41
 @property (nonatomic, assign) BOOL allowsInlineMediaPlayback;
@@ -47,12 +55,20 @@
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;
50 59
 @property (nonatomic, assign) BOOL allowFileAccessFromFileURLs;
51 60
 @property (nonatomic, assign) BOOL allowsLinkPreview;
52 61
 @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;
53 62
 @property (nonatomic, assign) BOOL showsVerticalScrollIndicator;
54 63
 @property (nonatomic, assign) BOOL directionalLockEnabled;
64
+@property (nonatomic, assign) BOOL ignoreSilentHardwareSwitch;
55 65
 @property (nonatomic, copy) NSString * _Nullable allowingReadAccessToURL;
66
+@property (nonatomic, assign) BOOL pullToRefreshEnabled;
67
+@property (nonatomic, weak) UIRefreshControl * refreshControl;
68
+
69
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
70
+@property (nonatomic, assign) WKContentMode contentMode;
71
+#endif
56 72
 
57 73
 + (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential;
58 74
 + (void)setCustomCertificatesForHost:(nullable NSDictionary *)certificates;
@@ -62,5 +78,7 @@
62 78
 - (void)goBack;
63 79
 - (void)reload;
64 80
 - (void)stopLoading;
81
+- (void)addPullToRefreshControl;
82
+- (void)pullToRefresh:(UIRefreshControl *)refreshControl;
65 83
 
66 84
 @end

apple/RNCWebView.m
文件差異過大導致無法顯示
查看文件


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


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

@@ -14,17 +14,14 @@
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)
17
+@implementation RCTConvert (WKWebView)
18
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
19
+RCT_ENUM_CONVERTER(WKContentMode, (@{
20
+    @"recommended": @(WKContentModeRecommended),
21
+    @"mobile": @(WKContentModeMobile),
22
+    @"desktop": @(WKContentModeDesktop),
23
+}), WKContentModeRecommended, integerValue)
26 24
 #endif
27
-
28 25
 @end
29 26
 
30 27
 @implementation RNCWebViewManager
@@ -35,7 +32,11 @@ RCT_ENUM_CONVERTER(UIScrollViewContentInsetAdjustmentBehavior, (@{
35 32
 
36 33
 RCT_EXPORT_MODULE()
37 34
 
35
+#if !TARGET_OS_OSX
38 36
 - (UIView *)view
37
+#else
38
+- (RCTUIView *)view
39
+#endif // !TARGET_OS_OSX
39 40
 {
40 41
   RNCWebView *webView = [RNCWebView new];
41 42
   webView.delegate = self;
@@ -43,6 +44,7 @@ RCT_EXPORT_MODULE()
43 44
 }
44 45
 
45 46
 RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
47
+RCT_EXPORT_VIEW_PROPERTY(onFileDownload, RCTDirectEventBlock)
46 48
 RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
47 49
 RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
48 50
 RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
@@ -52,7 +54,10 @@ RCT_EXPORT_VIEW_PROPERTY(onShouldStartLoadWithRequest, RCTDirectEventBlock)
52 54
 RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
53 55
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
54 56
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
57
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptForMainFrameOnly, BOOL)
58
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoadedForMainFrameOnly, BOOL)
55 59
 RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
60
+RCT_EXPORT_VIEW_PROPERTY(javaScriptCanOpenWindowsAutomatically, BOOL)
56 61
 RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
57 62
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
58 63
 RCT_EXPORT_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, BOOL)
@@ -61,6 +66,7 @@ RCT_EXPORT_VIEW_PROPERTY(dataDetectorTypes, WKDataDetectorTypes)
61 66
 #endif
62 67
 RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
63 68
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
69
+RCT_EXPORT_VIEW_PROPERTY(autoManageStatusBarEnabled, BOOL)
64 70
 RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL)
65 71
 RCT_EXPORT_VIEW_PROPERTY(allowsBackForwardNavigationGestures, BOOL)
66 72
 RCT_EXPORT_VIEW_PROPERTY(incognito, BOOL)
@@ -78,6 +84,10 @@ RCT_EXPORT_VIEW_PROPERTY(contentInsetAdjustmentBehavior, UIScrollViewContentInse
78 84
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
79 85
 #endif
80 86
 
87
+#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000 /* iOS 13 */
88
+RCT_EXPORT_VIEW_PROPERTY(contentMode, WKContentMode)
89
+#endif
90
+
81 91
 /**
82 92
  * Expose methods to enable messaging the webview.
83 93
  */
@@ -97,6 +107,10 @@ RCT_EXPORT_METHOD(postMessage:(nonnull NSNumber *)reactTag message:(NSString *)m
97 107
   }];
98 108
 }
99 109
 
110
+RCT_CUSTOM_VIEW_PROPERTY(pullToRefreshEnabled, BOOL, RNCWebView) {
111
+    view.pullToRefreshEnabled = json == nil ? false : [RCTConvert BOOL: json];
112
+}
113
+
100 114
 RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWebView) {
101 115
   view.bounces = json == nil ? true : [RCTConvert BOOL: json];
102 116
 }
@@ -113,9 +127,11 @@ RCT_CUSTOM_VIEW_PROPERTY(sharedCookiesEnabled, BOOL, RNCWebView) {
113 127
     view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json];
114 128
 }
115 129
 
130
+#if !TARGET_OS_OSX
116 131
 RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWebView) {
117 132
   view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json];
118 133
 }
134
+#endif // !TARGET_OS_OSX
119 135
 
120 136
 RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWebView) {
121 137
     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
 ```

+ 3
- 1
docs/Debugging.md 查看文件

@@ -15,12 +15,14 @@ It's possible to debug WebView contents in the iOS simulator or on a device usin
15 15
 3. Safari -> Develop -> [device name] -> [app name] -> [url - title]
16 16
 4. You can now debug the WebView contents just as you would on the web
17 17
 
18
-##### Note:
18
+##### Notes:
19 19
 
20 20
 When debugging on device you must enable Web Inspector in your device settings:
21 21
 
22 22
 Settings -> Safari -> Advanced -> Web Inspector
23 23
 
24
+Also, if you don't see your device in the Develop menu, and you started Safari before you started your simulator, try restarting Safari.
25
+
24 26
 ### Android & Chrome
25 27
 
26 28
 It's possible to debug WebView contents in the Android emulator or on a device using Chrome DevTools.

+ 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';

+ 69
- 67
docs/Guide.md 查看文件

@@ -48,33 +48,33 @@ import { WebView } from 'react-native-webview';
48 48
 
49 49
 class MyWeb extends Component {
50 50
   render() {
51
-    return (
52
-      <WebView source={{ uri: 'https://facebook.github.io/react-native/' }} />
53
-    );
51
+    return <WebView source={{ uri: 'https://reactnative.dev/' }} />;
54 52
   }
55 53
 }
56 54
 ```
57 55
 
58 56
 ### Loading local HTML files
59 57
 
60
-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, you can just import the html file like any other asset as shown below.
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.
61 63
 
62 64
 ```js
63 65
 import React, { Component } from 'react';
64 66
 import { WebView } from 'react-native-webview';
65 67
 
66
-const myHtmlFile = require("./my-asset-folder/local-site.html");
68
+const myHtmlFile = require('./my-asset-folder/local-site.html');
67 69
 
68 70
 class MyWeb extends Component {
69 71
   render() {
70
-    return (
71
-      <WebView source={myHtmlFile} />
72
-    );
72
+    return <WebView source={myHtmlFile} />;
73 73
   }
74 74
 }
75 75
 ```
76 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/src/main/assets/`. Then you can load the html file as shown in the following code block
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 78
 
79 79
 ```js
80 80
 import React, { Component } from 'react';
@@ -83,12 +83,14 @@ import { WebView } from 'react-native-webview';
83 83
 class MyWeb extends Component {
84 84
   render() {
85 85
     return (
86
-      <WebView source={{ uri: "file:///android_asset/local-site.html" }} />
86
+      <WebView source={{ uri: 'file:///android_asset/local-site.html' }} />
87 87
     );
88 88
   }
89 89
 }
90 90
 ```
91 91
 
92
+</details>
93
+
92 94
 ### Controlling navigation state changes
93 95
 
94 96
 Sometimes you want to intercept a user tapping on a link in your webview and do something different than navigating there in the webview. Here's some example code on how you might do that using the `onNavigationStateChange` function.
@@ -103,14 +105,14 @@ class MyWeb extends Component {
103 105
   render() {
104 106
     return (
105 107
       <WebView
106
-        ref={ref => (this.webview = ref)}
107
-        source={{ uri: 'https://facebook.github.io/react-native/' }}
108
+        ref={(ref) => (this.webview = ref)}
109
+        source={{ uri: 'https://reactnative.dev/' }}
108 110
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
109 111
       />
110 112
     );
111 113
   }
112 114
 
113
-  handleWebViewNavigationStateChange = newNavState => {
115
+  handleWebViewNavigationStateChange = (newNavState) => {
114 116
     // newNavState looks something like this:
115 117
     // {
116 118
     //   url?: string;
@@ -141,7 +143,7 @@ class MyWeb extends Component {
141 143
 
142 144
     // redirect somewhere else
143 145
     if (url.includes('google.com')) {
144
-      const newURL = 'https://facebook.github.io/react-native/';
146
+      const newURL = 'https://reactnative.dev/';
145 147
       const redirectTo = 'window.location = "' + newURL + '"';
146 148
       this.webview.injectJavaScript(redirectTo);
147 149
     }
@@ -149,44 +151,6 @@ class MyWeb extends Component {
149 151
 }
150 152
 ```
151 153
 
152
-#### Intercepting hash URL changes
153
-
154
-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`.
155
-
156
-You can inject some JavaScript to wrap the history functions in order to intercept these hash URL changes.
157
-
158
-```jsx
159
-<WebView
160
-  source={{ uri: someURI }}
161
-  injectedJavaScript={`
162
-    (function() {
163
-      function wrap(fn) {
164
-        return function wrapper() {
165
-          var res = fn.apply(this, arguments);
166
-          window.ReactNativeWebView.postMessage('navigationStateChange');
167
-          return res;
168
-        }
169
-      }
170
-
171
-      history.pushState = wrap(history.pushState);
172
-      history.replaceState = wrap(history.replaceState);
173
-      window.addEventListener('popstate', function() {
174
-        window.ReactNativeWebView.postMessage('navigationStateChange');
175
-      });
176
-    })();
177
-
178
-    true;
179
-  `}
180
-  onMessage={({ nativeEvent: state }) => {
181
-    if (state.data === 'navigationStateChange') {
182
-      // Navigation state updated, can check state.canGoBack, etc.
183
-    }
184
-  }}
185
-/>
186
-```
187
-
188
-Thanks to [Janic Duplessis](https://github.com/react-native-community/react-native-webview/issues/24#issuecomment-483956651) for this workaround.
189
-
190 154
 ### Add support for File Upload
191 155
 
192 156
 ##### iOS
@@ -229,6 +193,12 @@ Add permission in AndroidManifest.xml:
229 193
 </manifest>
230 194
 ```
231 195
 
196
+###### Camera option availability in uploading for Android
197
+
198
+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.
199
+
200
+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.
201
+
232 202
 ##### Check for File Upload support, with `static isFileUploadSupported()`
233 203
 
234 204
 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)):
@@ -262,9 +232,24 @@ You can control **single** or **multiple** file selection by specifing the [`mul
262 232
 
263 233
 ##### iOS
264 234
 
265
-For iOS, all you need to do is specify the permissions in your `ios/[project]/Info.plist` file:
235
+On iOS, you are going to have to supply your own code to download files. You can supply an `onFileDownload` callback
236
+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
237
+will be given to `onFileDownload`. From that callback you can then download that file however you would like to do so.
266 238
 
267
-Save to gallery:
239
+NOTE: iOS 13+ is needed for the best possible download experience. On iOS 13 Apple added an API for accessing HTTP response headers, which
240
+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
241
+trigger calls to `onFileDownload`.
242
+
243
+Example:
244
+
245
+```javascript
246
+onFileDownload = ({ nativeEvent }) => {
247
+  const { downloadUrl } = nativeEvent;
248
+  // --> Your download code goes here <--
249
+};
250
+```
251
+
252
+To be able to save images to the gallery you need to specify this permission in your `ios/[project]/Info.plist` file:
268 253
 
269 254
 ```
270 255
 <key>NSPhotoLibraryAddUsageDescription</key>
@@ -273,13 +258,14 @@ Save to gallery:
273 258
 
274 259
 ##### Android
275 260
 
276
-Add permission in AndroidManifest.xml:
261
+On Android, integration with the DownloadManager is built-in.
262
+Add this permisison in AndroidManifest.xml (only required if your app supports Android versions lower than 10):
277 263
 
278 264
 ```xml
279 265
 <manifest ...>
280 266
   ......
281 267
 
282
-  <!-- this is required to save files on Android  -->
268
+  <!-- this is required to save files on Android versions lower than 10 -->
283 269
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
284 270
 
285 271
   ......
@@ -319,6 +305,7 @@ export default class App extends Component {
319 305
             uri:
320 306
               'https://github.com/react-native-community/react-native-webview',
321 307
           }}
308
+          onMessage={(event) => {}}
322 309
           injectedJavaScript={runFirst}
323 310
         />
324 311
       </View>
@@ -327,19 +314,20 @@ export default class App extends Component {
327 314
 }
328 315
 ```
329 316
 
330
-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.
317
+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. An `onMessage` event is required as well to inject the JavaScript code into the WebView.
318
+
319
+By setting `injectedJavaScriptForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the main frame) if supported for the given platform. For example, if a page contains an iframe, the javascript will be injected into that iframe as well with this set to `false`. (Note this is not supported on Android.) There is also `injectedJavaScriptBeforeContentLoadedForMainFrameOnly` for injecting prior to content loading. Read more about this in the [Reference](./Reference.md#injectedjavascriptformainframeonly).
331 320
 
332 321
 <img alt="screenshot of Github repo" width="200" src="https://user-images.githubusercontent.com/1479215/53609254-e5dc9c00-3b7a-11e9-9118-bc4e520ce6ca.png" />
333 322
 
334 323
 _Under the hood_
335 324
 
336
-> On iOS, `injectedJavaScript` runs a method on WebView called `evaluateJavaScript:completionHandler:`
325
+> 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.
337 326
 > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
338 327
 
339
-
340 328
 #### The `injectedJavaScriptBeforeContentLoaded` prop
341 329
 
342
-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. 
330
+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.
343 331
 
344 332
 ```jsx
345 333
 import React, { Component } from 'react';
@@ -367,7 +355,13 @@ export default class App extends Component {
367 355
 }
368 356
 ```
369 357
 
370
-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. 
358
+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.
359
+
360
+By setting `injectedJavaScriptBeforeContentLoadedForMainFrameOnly: false`, the JavaScript injection will occur on all frames (not just the top frame) if supported for the given platform. However, 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.
361
+
362
+> 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.
363
+> On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
364
+> Note on Android Compatibility: For applications targeting `Build.VERSION_CODES.N` or later, JavaScript state from an empty WebView is no longer persisted across navigations like `loadUrl(java.lang.String)`. For example, global variables and functions defined before calling `loadUrl(java.lang.String)` will not exist in the loaded page. Applications should use the Android Native API `addJavascriptInterface(Object, String)` instead to persist JavaScript objects across navigations.
371 365
 
372 366
 #### The `injectJavaScript` method
373 367
 
@@ -392,7 +386,7 @@ export default class App extends Component {
392 386
     return (
393 387
       <View style={{ flex: 1 }}>
394 388
         <WebView
395
-          ref={r => (this.webref = r)}
389
+          ref={(r) => (this.webref = r)}
396 390
           source={{
397 391
             uri:
398 392
               'https://github.com/react-native-community/react-native-webview',
@@ -445,7 +439,7 @@ export default class App extends Component {
445 439
       <View style={{ flex: 1 }}>
446 440
         <WebView
447 441
           source={{ html }}
448
-          onMessage={event => {
442
+          onMessage={(event) => {
449 443
             alert(event.nativeEvent.data);
450 444
           }}
451 445
         />
@@ -481,7 +475,7 @@ This will set the header on the first load, but not on subsequent page navigatio
481 475
 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)):
482 476
 
483 477
 ```jsx
484
-const CustomHeaderWebView = props => {
478
+const CustomHeaderWebView = (props) => {
485 479
   const { uri, onLoadStart, ...restProps } = props;
486 480
   const [currentURI, setURI] = useState(props.source.uri);
487 481
   const newSource = { ...props.source, uri: currentURI };
@@ -490,7 +484,7 @@ const CustomHeaderWebView = props => {
490 484
     <WebView
491 485
       {...restProps}
492 486
       source={newSource}
493
-      onShouldStartLoadWithRequest={request => {
487
+      onShouldStartLoadWithRequest={(request) => {
494 488
         // If we're loading the current URI, allow it to load
495 489
         if (request.url === currentURI) return true;
496 490
         // We're loading a new URL -- change state first
@@ -513,9 +507,9 @@ const CustomHeaderWebView = props => {
513 507
 
514 508
 #### Managing Cookies
515 509
 
516
-You can set cookies on the React Native side using the [react-native-cookies](https://github.com/joeferraro/react-native-cookies) package.
510
+You can set cookies on the React Native side using the [@react-native-community/cookies](https://github.com/react-native-community/cookies) package.
517 511
 
518
-When you do, you'll likely want to enable the [sharedCookiesEnabled](Reference#sharedCookiesEnabled) prop as well.
512
+When you do, you'll likely want to enable the [sharedCookiesEnabled](Reference.md#sharedCookiesEnabled) prop as well.
519 513
 
520 514
 ```jsx
521 515
 const App = () => {
@@ -547,3 +541,11 @@ const App = () => {
547 541
 ```
548 542
 
549 543
 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).
544
+
545
+### Hardware Silence Switch
546
+
547
+There are some inconsistencies in how the hardware silence switch is handled between embedded `audio` and `video` elements and between iOS and Android platforms.
548
+
549
+Audio on `iOS` will be muted when the hardware silence switch is in the on position, unless the `ignoreSilentHardwareSwitch` parameter is set to true.
550
+
551
+Video on `iOS` will always ignore the hardware silence switch.

+ 7
- 5
docs/README.portuguese.md 查看文件

@@ -19,6 +19,8 @@ _Esse projeto é mantido gratuitamente por essas pessoas usando ambos seu tempo
19 19
 
20 20
 - [x] iOS
21 21
 - [x] Android
22
+- [x] macOS
23
+- [x] Windows
22 24
 
23 25
 _Nota: O suporte da Expo para o React Native WebView começou com [Expo SDK v33.0.0](https://blog.expo.io/expo-sdk-v33-0-0-is-now-available-52d1c99dfe4c)._
24 26
 
@@ -34,9 +36,11 @@ Esse projeto segue [versionamento semântico](https://semver.org/). Não hesitam
34 36
 
35 37
 Versão atual: ![version](https://img.shields.io/npm/v/react-native-webview.svg)
36 38
 
37
-- [7.0.1](https://github.com/react-native-community/react-native-webview/releases/tag/v7.0.1) - UIWebView removido
39
+- [8.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v8.0.0) - onNavigationStateChange agora é disparado quando alterado o hash da URL.
38 40
 
39
-- [6.0.**2**](https://github.com/react-native-community/react-native-webview/releases/tag/v6.0.2) - Update para AndroidX. Tenha certeza de habilitar no `android/gradle.properties` do seu projeto. Veja o [Getting Started Guide](docs/Getting-Started.md).
41
+- [7.0.1](https://github.com/react-native-community/react-native-webview/releases/tag/v7.0.1) - UIWebView removido.
42
+
43
+- [6.0.**2**](https://github.com/react-native-community/react-native-webview/releases/tag/v6.0.2) - Update para AndroidX. Tenha certeza de habilitar no `android/gradle.properties` do seu projeto. Veja o [Getting Started Guide](https://github.com/react-native-community/react-native-webview/blob/master/docs/Getting-Started.md).
40 44
 
41 45
 - [5.0.**1**](https://github.com/react-native-community/react-native-webview/releases/tag/v5.0.0) - Refatorou a antiga implementação postMessage para comunicação da visualização da webview para nativa.
42 46
 - [4.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v4.0.0) - Cache adicionada(habilitada por padrão).
@@ -62,9 +66,7 @@ import { WebView } from 'react-native-webview';
62 66
 // ...
63 67
 class MyWebComponent extends Component {
64 68
   render() {
65
-    return (
66
-      <WebView source={{ uri: 'https://facebook.github.io/react-native/' }} />
67
-    );
69
+    return <WebView source={{ uri: 'https://reactnative.dev/' }} />;
68 70
   }
69 71
 }
70 72
 ```

+ 379
- 177
docs/Reference.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
+}

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

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

二進制
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
+}

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

@@ -0,0 +1,161 @@
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: 400 }}>
39
+            <WebView
40
+              /**
41
+               * This HTML is a copy of the hosted multi-frame JS injection test.
42
+               * I have found that Android doesn't support beforeContentLoaded for a hosted HTML webpage, yet does for a static source.
43
+               * The cause of this is unresolved.
44
+               */
45
+              // source={{ html: HTML }}
46
+              source={{ uri: "https://birchlabs.co.uk/linguabrowse/infopages/obsol/rnw_iframe_test.html" }}
47
+              automaticallyAdjustContentInsets={false}
48
+              style={{backgroundColor:'#00000000'}}
49
+              
50
+              /* Must be populated in order for `messagingEnabled` to be `true` to activate the
51
+               * JS injection user scripts, consistent with current behaviour. This is undesirable,
52
+               * so needs addressing in a follow-up PR. */
53
+              onMessage={() => {}}
54
+              injectedJavaScriptBeforeContentLoadedForMainFrameOnly={false}
55
+              injectedJavaScriptForMainFrameOnly={false}
56
+
57
+              /* We set this property in each frame */
58
+              injectedJavaScriptBeforeContentLoaded={`
59
+              console.log("executing injectedJavaScriptBeforeContentLoaded... " + (new Date()).toString());
60
+              if(typeof window.top.injectedIframesBeforeContentLoaded === "undefined"){
61
+                window.top.injectedIframesBeforeContentLoaded = [];
62
+              }
63
+              window.self.colourToUse = "orange";
64
+              if(window.self === window.top){
65
+                console.log("Was window.top. window.frames.length is:", window.frames.length);
66
+                window.self.numberOfFramesAtBeforeContentLoaded = window.frames.length;
67
+                function declareSuccessOfBeforeContentLoaded(head){
68
+                  var style = window.self.document.createElement('style');
69
+                  style.type = 'text/css';
70
+                  style.innerHTML = "#before_failed { display: none !important; }#before_succeeded { display: inline-block !important; }";
71
+                  head.appendChild(style);
72
+                }
73
+
74
+                const head = (window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
75
+
76
+                if(head){
77
+                  declareSuccessOfBeforeContentLoaded(head);
78
+                } else {
79
+                  window.self.document.addEventListener("DOMContentLoaded", function (event) {
80
+                    const head = (window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
81
+                    declareSuccessOfBeforeContentLoaded(head);
82
+                  });
83
+                }
84
+              } else {
85
+                window.top.injectedIframesBeforeContentLoaded.push(window.self.name);
86
+                console.log("wasn't window.top.");
87
+                console.log("wasn't window.top. Still going...");
88
+              }
89
+              `}
90
+
91
+              /* We read the colourToUse property in each frame to recolour each frame */
92
+              injectedJavaScript={`
93
+              console.log("executing injectedJavaScript... " + (new Date()).toString());
94
+              if(typeof window.top.injectedIframesAfterContentLoaded === "undefined"){
95
+                window.top.injectedIframesAfterContentLoaded = [];
96
+              }
97
+
98
+              if(window.self.colourToUse){
99
+                window.self.document.body.style.backgroundColor = window.self.colourToUse;
100
+              } else {
101
+                window.self.document.body.style.backgroundColor = "cyan";
102
+              }
103
+
104
+              if(window.self === window.top){
105
+                function declareSuccessOfAfterContentLoaded(head){
106
+                  var style = window.self.document.createElement('style');
107
+                  style.type = 'text/css';
108
+                  style.innerHTML = "#after_failed { display: none !important; }#after_succeeded { display: inline-block !important; }";
109
+                  head.appendChild(style);
110
+                }
111
+
112
+                declareSuccessOfAfterContentLoaded(window.self.document.head || window.self.document.getElementsByTagName('head')[0]);
113
+
114
+                // var numberOfFramesAtBeforeContentLoadedEle = document.createElement('p');
115
+                // numberOfFramesAtBeforeContentLoadedEle.textContent = "Number of iframes upon the main frame's beforeContentLoaded: " +
116
+                // window.self.numberOfFramesAtBeforeContentLoaded;
117
+
118
+                // var numberOfFramesAtAfterContentLoadedEle = document.createElement('p');
119
+                // numberOfFramesAtAfterContentLoadedEle.textContent = "Number of iframes upon the main frame's afterContentLoaded: " + window.frames.length;
120
+                // numberOfFramesAtAfterContentLoadedEle.id = "numberOfFramesAtAfterContentLoadedEle";
121
+
122
+                var namedFramesAtBeforeContentLoadedEle = document.createElement('p');
123
+                namedFramesAtBeforeContentLoadedEle.textContent = "Names of iframes that called beforeContentLoaded: " + JSON.stringify(window.top.injectedIframesBeforeContentLoaded || []);
124
+                namedFramesAtBeforeContentLoadedEle.id = "namedFramesAtBeforeContentLoadedEle";
125
+
126
+                var namedFramesAtAfterContentLoadedEle = document.createElement('p');
127
+                namedFramesAtAfterContentLoadedEle.textContent = "Names of iframes that called afterContentLoaded: " + JSON.stringify(window.top.injectedIframesAfterContentLoaded);
128
+                namedFramesAtAfterContentLoadedEle.id = "namedFramesAtAfterContentLoadedEle";
129
+
130
+                // document.body.appendChild(numberOfFramesAtBeforeContentLoadedEle);
131
+                // document.body.appendChild(numberOfFramesAtAfterContentLoadedEle);
132
+                document.body.appendChild(namedFramesAtBeforeContentLoadedEle);
133
+                document.body.appendChild(namedFramesAtAfterContentLoadedEle);
134
+              } else {
135
+                window.top.injectedIframesAfterContentLoaded.push(window.self.name);
136
+                window.top.document.getElementById('namedFramesAtAfterContentLoadedEle').textContent = "Names of iframes that called afterContentLoaded: " + JSON.stringify(window.top.injectedIframesAfterContentLoaded);
137
+              }
138
+              `}
139
+            />
140
+          </View>
141
+        </View>
142
+        <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>
143
+        <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>
144
+        {/*<Text>1a) At injection time "beforeContentLoaded", a variable will be set in each frame to set 'orange' as the "colour to be used".</Text>*/}
145
+        {/*<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>*/}
146
+        {/*<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>*/}
147
+        {/*<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>*/}
148
+        <Text>✅ If the main frame becomes orange, then top-frame injection both beforeContentLoaded and afterContentLoaded is supported.</Text>
149
+        <Text>✅ If iframe_0, and iframe_1 become orange, then multi-frame injection beforeContentLoaded and afterContentLoaded is supported.</Text>
150
+        <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>
151
+        <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.</Text>
152
+        <Text>❌ If "Names of iframes that called beforeContentLoaded: " is [], then see above.</Text>
153
+        <Text>❌ If "Names of iframes that called afterContentLoaded: " is [], then afterContentLoaded is not supported in iframes.</Text>
154
+        <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>
155
+        <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>
156
+        <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>
157
+        <Text>❌ If the iframes remain their original colours (yellow and pink), then multi-frame injection is not supported at all.</Text>
158
+      </ScrollView>
159
+    );
160
+  }
161
+}

+ 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
+}

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


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