Просмотр исходного кода

Merge commit 'a482a74108' into fix-ios13-scrollIndicatorOffset

xietian 4 лет назад
Родитель
Сommit
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
             - node_modules-{{ arch }}-{{ checksum "yarn.lock" }}
34
             - node_modules-{{ arch }}-{{ checksum "yarn.lock" }}
35
 
35
 
36
       - run:
36
       - run:
37
-          name: Run Tests
37
+          name: Lint checks
38
           command: yarn ci
38
           command: yarn ci
39
 
39
 
40
   publish:
40
   publish:

+ 12
- 0
.gitattributes Просмотреть файл

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
 name: 'Detox CI Tests'
1
 name: 'Detox CI Tests'
2
-on: [push]
2
+on: [pull_request]
3
 
3
 
4
 jobs:
4
 jobs:
5
   tests:
5
   tests:

+ 108
- 0
.github/workflows/scripts/install-vs-features.ps1 Просмотреть файл

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 Просмотреть файл

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
 android/gradlew
53
 android/gradlew
54
 android/gradlew.bat
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
 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
4
 [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)
5
 [![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors)
5
 [![All Contributors](https://img.shields.io/badge/all_contributors-16-orange.svg?style=flat-square)](#contributors)
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)
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
 **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
 **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
 
20
 
20
 - [x] iOS
21
 - [x] iOS
21
 - [x] Android
22
 - [x] Android
23
+- [x] macOS
24
+- [x] Windows
22
 
25
 
23
 _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)._
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
 
37
 
35
 Current Version: ![version](https://img.shields.io/npm/v/react-native-webview.svg)
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
 - [7.0.1](https://github.com/react-native-community/react-native-webview/releases/tag/v7.0.1) - Removed UIWebView
42
 - [7.0.1](https://github.com/react-native-community/react-native-webview/releases/tag/v7.0.1) - Removed UIWebView
38
 
43
 
39
 - [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).
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
 // ...
66
 // ...
62
 class MyWebComponent extends Component {
67
 class MyWebComponent extends Component {
63
   render() {
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
 
97
 
95
 MIT
98
 MIT
96
 
99
 
97
-## Traduções
100
+## Translations
98
 
101
 
99
 This readme is available in:
102
 This readme is available in:
100
 
103
 
101
 - [Brazilian portuguese](docs/README.portuguese.md)
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 Просмотреть файл

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
 buildscript {
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
 def getExtOrIntegerDefault(name) {
30
 def getExtOrIntegerDefault(name) {
123
 
132
 
124
 dependencies {
133
 dependencies {
125
   //noinspection GradleDynamicVersion
134
   //noinspection GradleDynamicVersion
126
-  api 'com.facebook.react:react-native:+'
135
+  implementation 'com.facebook.react:react-native:+'
127
   implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
136
   implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
128
 }
137
 }

+ 2
- 2
android/gradle.properties Просмотреть файл

1
 ReactNativeWebView_kotlinVersion=1.3.50
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
 ReactNativeWebView_targetSdkVersion=28
4
 ReactNativeWebView_targetSdkVersion=28

+ 343
- 30
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewManager.java Просмотреть файл

4
 import android.annotation.TargetApi;
4
 import android.annotation.TargetApi;
5
 import android.app.DownloadManager;
5
 import android.app.DownloadManager;
6
 import android.content.Context;
6
 import android.content.Context;
7
-import android.content.Intent;
8
 import android.content.pm.ActivityInfo;
7
 import android.content.pm.ActivityInfo;
9
 import android.content.pm.PackageManager;
8
 import android.content.pm.PackageManager;
10
 import android.graphics.Bitmap;
9
 import android.graphics.Bitmap;
11
 import android.graphics.Color;
10
 import android.graphics.Color;
12
 import android.Manifest;
11
 import android.Manifest;
12
+import android.net.http.SslError;
13
 import android.net.Uri;
13
 import android.net.Uri;
14
 import android.os.Build;
14
 import android.os.Build;
15
 import android.os.Environment;
15
 import android.os.Environment;
16
-import androidx.annotation.RequiresApi;
17
-import androidx.core.content.ContextCompat;
16
+import android.os.SystemClock;
18
 import android.text.TextUtils;
17
 import android.text.TextUtils;
18
+import android.util.Log;
19
 import android.view.Gravity;
19
 import android.view.Gravity;
20
 import android.view.View;
20
 import android.view.View;
21
 import android.view.ViewGroup;
21
 import android.view.ViewGroup;
26
 import android.webkit.DownloadListener;
26
 import android.webkit.DownloadListener;
27
 import android.webkit.GeolocationPermissions;
27
 import android.webkit.GeolocationPermissions;
28
 import android.webkit.JavascriptInterface;
28
 import android.webkit.JavascriptInterface;
29
+import android.webkit.RenderProcessGoneDetail;
30
+import android.webkit.SslErrorHandler;
29
 import android.webkit.PermissionRequest;
31
 import android.webkit.PermissionRequest;
30
 import android.webkit.URLUtil;
32
 import android.webkit.URLUtil;
31
 import android.webkit.ValueCallback;
33
 import android.webkit.ValueCallback;
37
 import android.webkit.WebViewClient;
39
 import android.webkit.WebViewClient;
38
 import android.widget.FrameLayout;
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
 import com.facebook.react.views.scroll.ScrollEvent;
48
 import com.facebook.react.views.scroll.ScrollEvent;
41
 import com.facebook.react.views.scroll.ScrollEventType;
49
 import com.facebook.react.views.scroll.ScrollEventType;
42
 import com.facebook.react.views.scroll.OnScrollDispatchHelper;
50
 import com.facebook.react.views.scroll.OnScrollDispatchHelper;
43
 import com.facebook.react.bridge.Arguments;
51
 import com.facebook.react.bridge.Arguments;
52
+import com.facebook.react.bridge.CatalystInstance;
44
 import com.facebook.react.bridge.LifecycleEventListener;
53
 import com.facebook.react.bridge.LifecycleEventListener;
45
 import com.facebook.react.bridge.ReactContext;
54
 import com.facebook.react.bridge.ReactContext;
46
 import com.facebook.react.bridge.ReadableArray;
55
 import com.facebook.react.bridge.ReadableArray;
47
 import com.facebook.react.bridge.ReadableMap;
56
 import com.facebook.react.bridge.ReadableMap;
48
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
57
 import com.facebook.react.bridge.ReadableMapKeySetIterator;
49
 import com.facebook.react.bridge.WritableMap;
58
 import com.facebook.react.bridge.WritableMap;
59
+import com.facebook.react.bridge.WritableNativeArray;
60
+import com.facebook.react.bridge.WritableNativeMap;
50
 import com.facebook.react.common.MapBuilder;
61
 import com.facebook.react.common.MapBuilder;
51
 import com.facebook.react.common.build.ReactBuildConfig;
62
 import com.facebook.react.common.build.ReactBuildConfig;
52
 import com.facebook.react.module.annotations.ReactModule;
63
 import com.facebook.react.module.annotations.ReactModule;
57
 import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
68
 import com.facebook.react.uimanager.events.ContentSizeChangeEvent;
58
 import com.facebook.react.uimanager.events.Event;
69
 import com.facebook.react.uimanager.events.Event;
59
 import com.facebook.react.uimanager.events.EventDispatcher;
70
 import com.facebook.react.uimanager.events.EventDispatcher;
71
+import com.reactnativecommunity.webview.RNCWebViewModule.ShouldOverrideUrlLoadingLock.ShouldOverrideCallbackState;
60
 import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
72
 import com.reactnativecommunity.webview.events.TopLoadingErrorEvent;
61
 import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
73
 import com.reactnativecommunity.webview.events.TopHttpErrorEvent;
62
 import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
74
 import com.reactnativecommunity.webview.events.TopLoadingFinishEvent;
64
 import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
76
 import com.reactnativecommunity.webview.events.TopLoadingStartEvent;
65
 import com.reactnativecommunity.webview.events.TopMessageEvent;
77
 import com.reactnativecommunity.webview.events.TopMessageEvent;
66
 import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
78
 import com.reactnativecommunity.webview.events.TopShouldStartLoadWithRequestEvent;
79
+import com.reactnativecommunity.webview.events.TopRenderProcessGoneEvent;
67
 
80
 
68
 import org.json.JSONException;
81
 import org.json.JSONException;
69
 import org.json.JSONObject;
82
 import org.json.JSONObject;
76
 import java.util.HashMap;
89
 import java.util.HashMap;
77
 import java.util.Locale;
90
 import java.util.Locale;
78
 import java.util.Map;
91
 import java.util.Map;
79
-
80
-import javax.annotation.Nullable;
92
+import java.util.concurrent.atomic.AtomicReference;
81
 
93
 
82
 /**
94
 /**
83
  * Manages instances of {@link WebView}
95
  * Manages instances of {@link WebView}
105
  */
117
  */
106
 @ReactModule(name = RNCWebViewManager.REACT_CLASS)
118
 @ReactModule(name = RNCWebViewManager.REACT_CLASS)
107
 public class RNCWebViewManager extends SimpleViewManager<WebView> {
119
 public class RNCWebViewManager extends SimpleViewManager<WebView> {
120
+  private static final String TAG = "RNCWebViewManager";
108
 
121
 
109
-  public static String activeUrl = null;
110
   public static final int COMMAND_GO_BACK = 1;
122
   public static final int COMMAND_GO_BACK = 1;
111
   public static final int COMMAND_GO_FORWARD = 2;
123
   public static final int COMMAND_GO_FORWARD = 2;
112
   public static final int COMMAND_RELOAD = 3;
124
   public static final int COMMAND_RELOAD = 3;
129
   // Use `webView.loadUrl("about:blank")` to reliably reset the view
141
   // Use `webView.loadUrl("about:blank")` to reliably reset the view
130
   // state and release page resources (including any running JavaScript).
142
   // state and release page resources (including any running JavaScript).
131
   protected static final String BLANK_URL = "about:blank";
143
   protected static final String BLANK_URL = "about:blank";
144
+  protected static final int SHOULD_OVERRIDE_URL_LOADING_TIMEOUT = 250;
132
   protected WebViewConfig mWebViewConfig;
145
   protected WebViewConfig mWebViewConfig;
133
 
146
 
134
   protected RNCWebChromeClient mWebChromeClient = null;
147
   protected RNCWebChromeClient mWebChromeClient = null;
194
 
207
 
195
     webView.setDownloadListener(new DownloadListener() {
208
     webView.setDownloadListener(new DownloadListener() {
196
       public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
209
       public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
210
+        webView.setIgnoreErrFailedForThisURL(url);
211
+
197
         RNCWebViewModule module = getModule(reactContext);
212
         RNCWebViewModule module = getModule(reactContext);
198
 
213
 
199
         DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
214
         DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
208
           String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
223
           String baseUrl = urlObj.getProtocol() + "://" + urlObj.getHost();
209
           String cookie = CookieManager.getInstance().getCookie(baseUrl);
224
           String cookie = CookieManager.getInstance().getCookie(baseUrl);
210
           request.addRequestHeader("Cookie", cookie);
225
           request.addRequestHeader("Cookie", cookie);
211
-          System.out.println("Got cookie for DownloadManager: " + cookie);
212
         } catch (MalformedURLException e) {
226
         } catch (MalformedURLException e) {
213
           System.out.println("Error getting cookie for DownloadManager: " + e.toString());
227
           System.out.println("Error getting cookie for DownloadManager: " + e.toString());
214
           e.printStackTrace();
228
           e.printStackTrace();
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
   @ReactProp(name = "overScrollMode")
323
   @ReactProp(name = "overScrollMode")
295
   public void setOverScrollMode(WebView view, String overScrollModeString) {
324
   public void setOverScrollMode(WebView view, String overScrollModeString) {
296
     Integer overScrollMode;
325
     Integer overScrollMode;
372
     view.getSettings().setMediaPlaybackRequiresUserGesture(requires);
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
   @ReactProp(name = "allowFileAccessFromFileURLs")
409
   @ReactProp(name = "allowFileAccessFromFileURLs")
376
   public void setAllowFileAccessFromFileURLs(WebView view, boolean allow) {
410
   public void setAllowFileAccessFromFileURLs(WebView view, boolean allow) {
377
     view.getSettings().setAllowFileAccessFromFileURLs(allow);
411
     view.getSettings().setAllowFileAccessFromFileURLs(allow);
392
     ((RNCWebView) view).setInjectedJavaScript(injectedJavaScript);
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
   @ReactProp(name = "messagingEnabled")
444
   @ReactProp(name = "messagingEnabled")
396
   public void setMessagingEnabled(WebView view, boolean enabled) {
445
   public void setMessagingEnabled(WebView view, boolean enabled) {
397
     ((RNCWebView) view).setMessagingEnabled(enabled);
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
   @ReactProp(name = "incognito")
454
   @ReactProp(name = "incognito")
401
   public void setIncognito(WebView view, boolean enabled) {
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
     // Remove all previous cookies
461
     // Remove all previous cookies
403
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
462
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
404
       CookieManager.getInstance().removeAllCookies(null);
463
       CookieManager.getInstance().removeAllCookies(null);
408
 
467
 
409
     // Disable caching
468
     // Disable caching
410
     view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
469
     view.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
411
-    view.getSettings().setAppCacheEnabled(!enabled);
470
+    view.getSettings().setAppCacheEnabled(false);
412
     view.clearHistory();
471
     view.clearHistory();
413
-    view.clearCache(enabled);
472
+    view.clearCache(true);
414
 
473
 
415
     // No form data or autofill enabled
474
     // No form data or autofill enabled
416
     view.clearFormData();
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
   @ReactProp(name = "source")
480
   @ReactProp(name = "source")
545
     export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
604
     export.put(TopShouldStartLoadWithRequestEvent.EVENT_NAME, MapBuilder.of("registrationName", "onShouldStartLoadWithRequest"));
546
     export.put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll"));
605
     export.put(ScrollEventType.getJSEventName(ScrollEventType.SCROLL), MapBuilder.of("registrationName", "onScroll"));
547
     export.put(TopHttpErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onHttpError"));
606
     export.put(TopHttpErrorEvent.EVENT_NAME, MapBuilder.of("registrationName", "onHttpError"));
607
+    export.put(TopRenderProcessGoneEvent.EVENT_NAME, MapBuilder.of("registrationName", "onRenderProcessGone"));
548
     return export;
608
     return export;
549
   }
609
   }
550
 
610
 
609
         if (args == null) {
669
         if (args == null) {
610
           throw new RuntimeException("Arguments for loading an url are null!");
670
           throw new RuntimeException("Arguments for loading an url are null!");
611
         }
671
         }
672
+        ((RNCWebView) root).progressChangedFilter.setWaitingForCommandLoadUrl(false);
612
         root.loadUrl(args.getString(0));
673
         root.loadUrl(args.getString(0));
613
         break;
674
         break;
614
       case COMMAND_FOCUS:
675
       case COMMAND_FOCUS:
646
         public Bitmap getDefaultVideoPoster() {
707
         public Bitmap getDefaultVideoPoster() {
647
           return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
708
           return Bitmap.createBitmap(50, 50, Bitmap.Config.ARGB_8888);
648
         }
709
         }
649
-        
710
+
650
         @Override
711
         @Override
651
         public void onShowCustomView(View view, CustomViewCallback callback) {
712
         public void onShowCustomView(View view, CustomViewCallback callback) {
652
           if (mVideoView != null) {
713
           if (mVideoView != null) {
714
     protected boolean mLastLoadFailed = false;
775
     protected boolean mLastLoadFailed = false;
715
     protected @Nullable
776
     protected @Nullable
716
     ReadableArray mUrlPrefixesForDefaultIntent;
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
     @Override
785
     @Override
719
     public void onPageFinished(WebView webView, String url) {
786
     public void onPageFinished(WebView webView, String url) {
733
       super.onPageStarted(webView, url, favicon);
800
       super.onPageStarted(webView, url, favicon);
734
       mLastLoadFailed = false;
801
       mLastLoadFailed = false;
735
 
802
 
803
+      RNCWebView reactWebView = (RNCWebView) webView;
804
+      reactWebView.callInjectedJavaScriptBeforeContentLoaded();
805
+
736
       dispatchEvent(
806
       dispatchEvent(
737
         webView,
807
         webView,
738
         new TopLoadingStartEvent(
808
         new TopLoadingStartEvent(
742
 
812
 
743
     @Override
813
     @Override
744
     public boolean shouldOverrideUrlLoading(WebView view, String url) {
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
     @TargetApi(Build.VERSION_CODES.N)
862
     @TargetApi(Build.VERSION_CODES.N)
756
     @Override
863
     @Override
759
       return this.shouldOverrideUrlLoading(view, url);
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
     @Override
913
     @Override
763
     public void onReceivedError(
914
     public void onReceivedError(
764
       WebView webView,
915
       WebView webView,
765
       int errorCode,
916
       int errorCode,
766
       String description,
917
       String description,
767
       String failingUrl) {
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
       super.onReceivedError(webView, errorCode, description, failingUrl);
934
       super.onReceivedError(webView, errorCode, description, failingUrl);
769
       mLastLoadFailed = true;
935
       mLastLoadFailed = true;
770
 
936
 
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
     protected void emitFinishEvent(WebView webView, String url) {
1004
     protected void emitFinishEvent(WebView webView, String url) {
804
       dispatchEvent(
1005
       dispatchEvent(
805
         webView,
1006
         webView,
824
     public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
1025
     public void setUrlPrefixesForDefaultIntent(ReadableArray specialUrls) {
825
       mUrlPrefixesForDefaultIntent = specialUrls;
1026
       mUrlPrefixesForDefaultIntent = specialUrls;
826
     }
1027
     }
1028
+
1029
+    public void setProgressChangedFilter(RNCWebView.ProgressChangedFilter filter) {
1030
+      progressChangedFilter = filter;
1031
+    }
827
   }
1032
   }
828
 
1033
 
829
   protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
1034
   protected static class RNCWebChromeClient extends WebChromeClient implements LifecycleEventListener {
845
     protected View mVideoView;
1050
     protected View mVideoView;
846
     protected WebChromeClient.CustomViewCallback mCustomViewCallback;
1051
     protected WebChromeClient.CustomViewCallback mCustomViewCallback;
847
 
1052
 
1053
+    protected RNCWebView.ProgressChangedFilter progressChangedFilter = null;
1054
+
848
     public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
1055
     public RNCWebChromeClient(ReactContext reactContext, WebView webView) {
849
       this.mReactContext = reactContext;
1056
       this.mReactContext = reactContext;
850
       this.mWebView = webView;
1057
       this.mWebView = webView;
899
     public void onProgressChanged(WebView webView, int newProgress) {
1106
     public void onProgressChanged(WebView webView, int newProgress) {
900
       super.onProgressChanged(webView, newProgress);
1107
       super.onProgressChanged(webView, newProgress);
901
       final String url = webView.getUrl();
1108
       final String url = webView.getUrl();
902
-      if (
903
-        url != null
904
-        && activeUrl != null
905
-        && !url.equals(activeUrl)
906
-      ) {
1109
+      if (progressChangedFilter.isWaitingForCommandLoadUrl()) {
907
         return;
1110
         return;
908
       }
1111
       }
909
       WritableMap event = Arguments.createMap();
1112
       WritableMap event = Arguments.createMap();
942
     public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
1145
     public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
943
       String[] acceptTypes = fileChooserParams.getAcceptTypes();
1146
       String[] acceptTypes = fileChooserParams.getAcceptTypes();
944
       boolean allowMultiple = fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE;
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
     @Override
1151
     @Override
962
     protected ViewGroup getRootView() {
1164
     protected ViewGroup getRootView() {
963
       return (ViewGroup) mReactContext.getCurrentActivity().findViewById(android.R.id.content);
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
   protected static class RNCWebView extends WebView implements LifecycleEventListener {
1177
   protected static class RNCWebView extends WebView implements LifecycleEventListener {
972
     protected @Nullable
1178
     protected @Nullable
973
     String injectedJS;
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
     protected boolean messagingEnabled = false;
1190
     protected boolean messagingEnabled = false;
975
     protected @Nullable
1191
     protected @Nullable
1192
+    String messagingModuleName;
1193
+    protected @Nullable
976
     RNCWebViewClient mRNCWebViewClient;
1194
     RNCWebViewClient mRNCWebViewClient;
1195
+    protected @Nullable
1196
+    CatalystInstance mCatalystInstance;
977
     protected boolean sendContentSizeChangeEvents = false;
1197
     protected boolean sendContentSizeChangeEvents = false;
978
     private OnScrollDispatchHelper mOnScrollDispatchHelper;
1198
     private OnScrollDispatchHelper mOnScrollDispatchHelper;
979
     protected boolean hasScrollEvent = false;
1199
     protected boolean hasScrollEvent = false;
1200
+    protected ProgressChangedFilter progressChangedFilter;
980
 
1201
 
981
     /**
1202
     /**
982
      * WebView must be created with an context of the current activity
1203
      * WebView must be created with an context of the current activity
986
      */
1207
      */
987
     public RNCWebView(ThemedReactContext reactContext) {
1208
     public RNCWebView(ThemedReactContext reactContext) {
988
       super(reactContext);
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
     public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
1218
     public void setSendContentSizeChangeEvents(boolean sendContentSizeChangeEvents) {
1032
       super.setWebViewClient(client);
1259
       super.setWebViewClient(client);
1033
       if (client instanceof RNCWebViewClient) {
1260
       if (client instanceof RNCWebViewClient) {
1034
         mRNCWebViewClient = (RNCWebViewClient) client;
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
       injectedJS = js;
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
     protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1297
     protected RNCWebViewBridge createRNCWebViewBridge(RNCWebView webView) {
1048
       return new RNCWebViewBridge(webView);
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
     @SuppressLint("AddJavascriptInterface")
1309
     @SuppressLint("AddJavascriptInterface")
1052
     public void setMessagingEnabled(boolean enabled) {
1310
     public void setMessagingEnabled(boolean enabled) {
1053
       if (messagingEnabled == enabled) {
1311
       if (messagingEnabled == enabled) {
1063
       }
1321
       }
1064
     }
1322
     }
1065
 
1323
 
1324
+    public void setMessagingModuleName(String moduleName) {
1325
+      messagingModuleName = moduleName;
1326
+    }
1327
+
1066
     protected void evaluateJavascriptWithFallback(String script) {
1328
     protected void evaluateJavascriptWithFallback(String script) {
1067
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
1329
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
1068
         evaluateJavascript(script, null);
1330
         evaluateJavascript(script, null);
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
     public void onMessage(String message) {
1358
     public void onMessage(String message) {
1359
+      ReactContext reactContext = (ReactContext) this.getContext();
1360
+      RNCWebView mContext = this;
1361
+
1089
       if (mRNCWebViewClient != null) {
1362
       if (mRNCWebViewClient != null) {
1090
         WebView webView = this;
1363
         WebView webView = this;
1091
         webView.post(new Runnable() {
1364
         webView.post(new Runnable() {
1096
             }
1369
             }
1097
             WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
1370
             WritableMap data = mRNCWebViewClient.createWebViewEvent(webView, webView.getUrl());
1098
             data.putString("data", message);
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
       } else {
1380
       } else {
1103
         WritableMap eventData = Arguments.createMap();
1381
         WritableMap eventData = Arguments.createMap();
1104
         eventData.putString("data", message);
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
     protected void onScrollChanged(int x, int y, int oldX, int oldY) {
1402
     protected void onScrollChanged(int x, int y, int oldX, int oldY) {
1110
       super.onScrollChanged(x, y, oldX, oldY);
1403
       super.onScrollChanged(x, y, oldX, oldY);
1111
 
1404
 
1139
       destroy();
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
     protected class RNCWebViewBridge {
1443
     protected class RNCWebViewBridge {
1143
       RNCWebView mContext;
1444
       RNCWebView mContext;
1144
 
1445
 
1155
         mContext.onMessage(message);
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
 import android.os.Environment;
11
 import android.os.Environment;
12
 import android.os.Parcelable;
12
 import android.os.Parcelable;
13
 import android.provider.MediaStore;
13
 import android.provider.MediaStore;
14
+
15
+import androidx.annotation.Nullable;
14
 import androidx.annotation.RequiresApi;
16
 import androidx.annotation.RequiresApi;
15
 import androidx.core.content.ContextCompat;
17
 import androidx.core.content.ContextCompat;
16
 import androidx.core.content.FileProvider;
18
 import androidx.core.content.FileProvider;
19
+import androidx.core.util.Pair;
20
+
17
 import android.util.Log;
21
 import android.util.Log;
18
 import android.webkit.MimeTypeMap;
22
 import android.webkit.MimeTypeMap;
19
 import android.webkit.ValueCallback;
23
 import android.webkit.ValueCallback;
32
 import java.io.File;
36
 import java.io.File;
33
 import java.io.IOException;
37
 import java.io.IOException;
34
 import java.util.ArrayList;
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
 import static android.app.Activity.RESULT_OK;
43
 import static android.app.Activity.RESULT_OK;
37
 
44
 
41
   private static final int PICKER = 1;
48
   private static final int PICKER = 1;
42
   private static final int PICKER_LEGACY = 3;
49
   private static final int PICKER_LEGACY = 3;
43
   private static final int FILE_DOWNLOAD_PERMISSION_REQUEST = 1;
50
   private static final int FILE_DOWNLOAD_PERMISSION_REQUEST = 1;
44
-  final String DEFAULT_MIME_TYPES = "*/*";
45
   private ValueCallback<Uri> filePathCallbackLegacy;
51
   private ValueCallback<Uri> filePathCallbackLegacy;
46
   private ValueCallback<Uri[]> filePathCallback;
52
   private ValueCallback<Uri[]> filePathCallback;
47
-  private Uri outputFileUri;
53
+  private File outputImage;
54
+  private File outputVideo;
48
   private DownloadManager.Request downloadRequest;
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
   private PermissionListener webviewFileDownloaderPermissionListener = new PermissionListener() {
98
   private PermissionListener webviewFileDownloaderPermissionListener = new PermissionListener() {
50
     @Override
99
     @Override
51
     public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
100
     public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
89
     promise.resolve(result);
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
   public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
152
   public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
93
 
153
 
94
     if (filePathCallback == null && filePathCallbackLegacy == null) {
154
     if (filePathCallback == null && filePathCallbackLegacy == null) {
95
       return;
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
     // based off of which button was pressed, we get an activity result and a file
168
     // based off of which button was pressed, we get an activity result and a file
99
     // the camera activity doesn't properly return the filename* (I think?) so we use
169
     // the camera activity doesn't properly return the filename* (I think?) so we use
100
     // this filename instead
170
     // this filename instead
105
             filePathCallback.onReceiveValue(null);
175
             filePathCallback.onReceiveValue(null);
106
           }
176
           }
107
         } else {
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
           } else {
182
           } else {
112
-            filePathCallback.onReceiveValue(new Uri[]{outputFileUri});
183
+            filePathCallback.onReceiveValue(this.getSelectedFiles(data, resultCode));
113
           }
184
           }
114
         }
185
         }
115
         break;
186
         break;
116
       case PICKER_LEGACY:
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
         break;
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
     filePathCallback = null;
210
     filePathCallback = null;
123
     filePathCallbackLegacy = null;
211
     filePathCallbackLegacy = null;
124
-    outputFileUri = null;
212
+    outputImage = null;
213
+    outputVideo = null;
125
   }
214
   }
126
 
215
 
127
   public void onNewIntent(Intent intent) {
216
   public void onNewIntent(Intent intent) {
132
       return null;
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
     // we have multiple files selected
224
     // we have multiple files selected
145
     if (data.getClipData() != null) {
225
     if (data.getClipData() != null) {
146
       final int numSelectedFiles = data.getClipData().getItemCount();
226
       final int numSelectedFiles = data.getClipData().getItemCount();
150
       }
230
       }
151
       return result;
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
     return null;
239
     return null;
154
   }
240
   }
155
 
241
 
161
 
247
 
162
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
248
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
163
     if (acceptsImages(acceptType)) {
249
     if (acceptsImages(acceptType)) {
164
-      extraIntents.add(getPhotoIntent());
250
+      Intent photoIntent = getPhotoIntent();
251
+      if (photoIntent != null) {
252
+        extraIntents.add(photoIntent);
253
+      }
165
     }
254
     }
166
     if (acceptsVideo(acceptType)) {
255
     if (acceptsVideo(acceptType)) {
167
-      extraIntents.add(getVideoIntent());
256
+      Intent videoIntent = getVideoIntent();
257
+      if (videoIntent != null) {
258
+        extraIntents.add(videoIntent);
259
+      }
168
     }
260
     }
169
     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
261
     chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, extraIntents.toArray(new Parcelable[]{}));
170
 
262
 
176
   }
268
   }
177
 
269
 
178
   @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
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
     filePathCallback = callback;
272
     filePathCallback = callback;
181
 
273
 
182
     ArrayList<Parcelable> extraIntents = new ArrayList<>();
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
     Intent fileSelectionIntent = getFileChooserIntent(acceptTypes, allowMultiple);
290
     Intent fileSelectionIntent = getFileChooserIntent(acceptTypes, allowMultiple);
216
   }
316
   }
217
 
317
 
218
   public boolean grantFileDownloaderPermissions() {
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
       return true;
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
       PermissionAwareActivity activity = getPermissionAwareActivity();
326
       PermissionAwareActivity activity = getPermissionAwareActivity();
230
       activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, FILE_DOWNLOAD_PERMISSION_REQUEST, webviewFileDownloaderPermissionListener);
327
       activity.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, FILE_DOWNLOAD_PERMISSION_REQUEST, webviewFileDownloaderPermissionListener);
231
     }
328
     }
233
     return result;
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
   private Intent getPhotoIntent() {
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
     return intent;
363
     return intent;
241
   }
364
   }
242
 
365
 
243
   private Intent getVideoIntent() {
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
     return intent;
379
     return intent;
248
   }
380
   }
249
 
381
 
250
   private Intent getFileChooserIntent(String acceptTypes) {
382
   private Intent getFileChooserIntent(String acceptTypes) {
251
     String _acceptTypes = acceptTypes;
383
     String _acceptTypes = acceptTypes;
252
     if (acceptTypes.isEmpty()) {
384
     if (acceptTypes.isEmpty()) {
253
-      _acceptTypes = DEFAULT_MIME_TYPES;
385
+      _acceptTypes = MimeType.DEFAULT.value;
254
     }
386
     }
255
     if (acceptTypes.matches("\\.\\w+")) {
387
     if (acceptTypes.matches("\\.\\w+")) {
256
       _acceptTypes = getMimeTypeFromExtension(acceptTypes.replace(".", ""));
388
       _acceptTypes = getMimeTypeFromExtension(acceptTypes.replace(".", ""));
264
   private Intent getFileChooserIntent(String[] acceptTypes, boolean allowMultiple) {
396
   private Intent getFileChooserIntent(String[] acceptTypes, boolean allowMultiple) {
265
     Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
397
     Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
266
     intent.addCategory(Intent.CATEGORY_OPENABLE);
398
     intent.addCategory(Intent.CATEGORY_OPENABLE);
267
-    intent.setType("*/*");
399
+    intent.setType(MimeType.DEFAULT.value);
268
     intent.putExtra(Intent.EXTRA_MIME_TYPES, getAcceptedMimeType(acceptTypes));
400
     intent.putExtra(Intent.EXTRA_MIME_TYPES, getAcceptedMimeType(acceptTypes));
269
     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
401
     intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, allowMultiple);
270
     return intent;
402
     return intent;
275
     if (types.matches("\\.\\w+")) {
407
     if (types.matches("\\.\\w+")) {
276
       mimeType = getMimeTypeFromExtension(types.replace(".", ""));
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
   private Boolean acceptsImages(String[] types) {
413
   private Boolean acceptsImages(String[] types) {
282
     String[] mimeTypes = getAcceptedMimeType(types);
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
   private Boolean acceptsVideo(String types) {
418
   private Boolean acceptsVideo(String types) {
419
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
420
+      return false;
421
+    }
422
+
287
     String mimeType = types;
423
     String mimeType = types;
288
     if (types.matches("\\.\\w+")) {
424
     if (types.matches("\\.\\w+")) {
289
       mimeType = getMimeTypeFromExtension(types.replace(".", ""));
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
   private Boolean acceptsVideo(String[] types) {
430
   private Boolean acceptsVideo(String[] types) {
431
+    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
432
+      return false;
433
+    }
434
+
295
     String[] mimeTypes = getAcceptedMimeType(types);
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
   private Boolean arrayContainsString(String[] array, String pattern) {
439
   private Boolean arrayContainsString(String[] array, String pattern) {
306
   }
446
   }
307
 
447
 
308
   private String[] getAcceptedMimeType(String[] types) {
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
     String[] mimeTypes = new String[types.length];
452
     String[] mimeTypes = new String[types.length];
313
     for (int i = 0; i < types.length; i++) {
453
     for (int i = 0; i < types.length; i++) {
315
       // convert file extensions to mime types
455
       // convert file extensions to mime types
316
       if (t.matches("\\.\\w+")) {
456
       if (t.matches("\\.\\w+")) {
317
         String mimeType = getMimeTypeFromExtension(t.replace(".", ""));
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
       } else {
463
       } else {
320
         mimeTypes[i] = t;
464
         mimeTypes[i] = t;
321
       }
465
       }
331
     return type;
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
     // for versions below 6.0 (23) we use the old File creation & permissions model
479
     // for versions below 6.0 (23) we use the old File creation & permissions model
344
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
480
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
345
       return Uri.fromFile(capturedFile);
481
       return Uri.fromFile(capturedFile);
350
     return FileProvider.getUriForFile(getReactApplicationContext(), packageName + ".fileprovider", capturedFile);
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
     String prefix = "";
490
     String prefix = "";
355
     String suffix = "";
491
     String suffix = "";
356
     String dir = "";
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
     // for versions below 6.0 (23) we use the old File creation & permissions model
513
     // for versions below 6.0 (23) we use the old File creation & permissions model
372
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
514
     if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
373
       // only this Directory works on all tested Android versions
515
       // only this Directory works on all tested Android versions
374
       // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
516
       // ctx.getExternalFilesDir(dir) was failing on Android 5.0 (sdk 21)
375
       File storageDir = Environment.getExternalStoragePublicDirectory(dir);
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
     // when our array returned from getAcceptTypes() has no values set from the webview
528
     // when our array returned from getAcceptTypes() has no values set from the webview
385
     // i.e. <input type="file" />, without any "accept" attr
529
     // i.e. <input type="file" />, without any "accept" attr
386
     // will be an array with one empty string element, afaik
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
   private PermissionAwareActivity getPermissionAwareActivity() {
535
   private PermissionAwareActivity getPermissionAwareActivity() {

+ 0
- 27
android/src/main/java/com/reactnativecommunity/webview/RNCWebViewPackage.java Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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
 
14
 
15
   init {
15
   init {
16
     mData.putString("navigationType", "other")
16
     mData.putString("navigationType", "other")
17
+    // Android does not raise shouldOverrideUrlLoading for inner frames
18
+    mData.putBoolean("isTopFrame", true)
17
   }
19
   }
18
 
20
 
19
   override fun getEventName(): String = EVENT_NAME
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
 
19
 
20
 @end
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
 @interface RNCWebView : RCTView
27
 @interface RNCWebView : RCTView
23
 
28
 
24
 @property (nonatomic, weak) id<RNCWebViewDelegate> _Nullable delegate;
29
 @property (nonatomic, weak) id<RNCWebViewDelegate> _Nullable delegate;
26
 @property (nonatomic, assign) BOOL messagingEnabled;
31
 @property (nonatomic, assign) BOOL messagingEnabled;
27
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScript;
32
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScript;
28
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScriptBeforeContentLoaded;
33
 @property (nonatomic, copy) NSString * _Nullable injectedJavaScriptBeforeContentLoaded;
34
+@property (nonatomic, assign) BOOL injectedJavaScriptForMainFrameOnly;
35
+@property (nonatomic, assign) BOOL injectedJavaScriptBeforeContentLoadedForMainFrameOnly;
29
 @property (nonatomic, assign) BOOL scrollEnabled;
36
 @property (nonatomic, assign) BOOL scrollEnabled;
30
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
37
 @property (nonatomic, assign) BOOL sharedCookiesEnabled;
38
+@property (nonatomic, assign) BOOL autoManageStatusBarEnabled;
31
 @property (nonatomic, assign) BOOL pagingEnabled;
39
 @property (nonatomic, assign) BOOL pagingEnabled;
32
 @property (nonatomic, assign) CGFloat decelerationRate;
40
 @property (nonatomic, assign) CGFloat decelerationRate;
33
 @property (nonatomic, assign) BOOL allowsInlineMediaPlayback;
41
 @property (nonatomic, assign) BOOL allowsInlineMediaPlayback;
47
 @property (nonatomic, copy) NSString * _Nullable applicationNameForUserAgent;
55
 @property (nonatomic, copy) NSString * _Nullable applicationNameForUserAgent;
48
 @property (nonatomic, assign) BOOL cacheEnabled;
56
 @property (nonatomic, assign) BOOL cacheEnabled;
49
 @property (nonatomic, assign) BOOL javaScriptEnabled;
57
 @property (nonatomic, assign) BOOL javaScriptEnabled;
58
+@property (nonatomic, assign) BOOL javaScriptCanOpenWindowsAutomatically;
50
 @property (nonatomic, assign) BOOL allowFileAccessFromFileURLs;
59
 @property (nonatomic, assign) BOOL allowFileAccessFromFileURLs;
51
 @property (nonatomic, assign) BOOL allowsLinkPreview;
60
 @property (nonatomic, assign) BOOL allowsLinkPreview;
52
 @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;
61
 @property (nonatomic, assign) BOOL showsHorizontalScrollIndicator;
53
 @property (nonatomic, assign) BOOL showsVerticalScrollIndicator;
62
 @property (nonatomic, assign) BOOL showsVerticalScrollIndicator;
54
 @property (nonatomic, assign) BOOL directionalLockEnabled;
63
 @property (nonatomic, assign) BOOL directionalLockEnabled;
64
+@property (nonatomic, assign) BOOL ignoreSilentHardwareSwitch;
55
 @property (nonatomic, copy) NSString * _Nullable allowingReadAccessToURL;
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
 + (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential;
73
 + (void)setClientAuthenticationCredential:(nullable NSURLCredential*)credential;
58
 + (void)setCustomCertificatesForHost:(nullable NSDictionary *)certificates;
74
 + (void)setCustomCertificatesForHost:(nullable NSDictionary *)certificates;
62
 - (void)goBack;
78
 - (void)goBack;
63
 - (void)reload;
79
 - (void)reload;
64
 - (void)stopLoading;
80
 - (void)stopLoading;
81
+- (void)addPullToRefreshControl;
82
+- (void)pullToRefresh:(UIRefreshControl *)refreshControl;
65
 
83
 
66
 @end
84
 @end

apple/RNCWebView.m
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


ios/RNCWebViewManager.h → apple/RNCWebViewManager.h Просмотреть файл


ios/RNCWebViewManager.m → apple/RNCWebViewManager.m Просмотреть файл

14
 @interface RNCWebViewManager () <RNCWebViewDelegate>
14
 @interface RNCWebViewManager () <RNCWebViewDelegate>
15
 @end
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
 #endif
24
 #endif
27
-
28
 @end
25
 @end
29
 
26
 
30
 @implementation RNCWebViewManager
27
 @implementation RNCWebViewManager
35
 
32
 
36
 RCT_EXPORT_MODULE()
33
 RCT_EXPORT_MODULE()
37
 
34
 
35
+#if !TARGET_OS_OSX
38
 - (UIView *)view
36
 - (UIView *)view
37
+#else
38
+- (RCTUIView *)view
39
+#endif // !TARGET_OS_OSX
39
 {
40
 {
40
   RNCWebView *webView = [RNCWebView new];
41
   RNCWebView *webView = [RNCWebView new];
41
   webView.delegate = self;
42
   webView.delegate = self;
43
 }
44
 }
44
 
45
 
45
 RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
46
 RCT_EXPORT_VIEW_PROPERTY(source, NSDictionary)
47
+RCT_EXPORT_VIEW_PROPERTY(onFileDownload, RCTDirectEventBlock)
46
 RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
48
 RCT_EXPORT_VIEW_PROPERTY(onLoadingStart, RCTDirectEventBlock)
47
 RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
49
 RCT_EXPORT_VIEW_PROPERTY(onLoadingFinish, RCTDirectEventBlock)
48
 RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
50
 RCT_EXPORT_VIEW_PROPERTY(onLoadingError, RCTDirectEventBlock)
52
 RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
54
 RCT_EXPORT_VIEW_PROPERTY(onContentProcessDidTerminate, RCTDirectEventBlock)
53
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
55
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScript, NSString)
54
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
56
 RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoaded, NSString)
57
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptForMainFrameOnly, BOOL)
58
+RCT_EXPORT_VIEW_PROPERTY(injectedJavaScriptBeforeContentLoadedForMainFrameOnly, BOOL)
55
 RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
59
 RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
60
+RCT_EXPORT_VIEW_PROPERTY(javaScriptCanOpenWindowsAutomatically, BOOL)
56
 RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
61
 RCT_EXPORT_VIEW_PROPERTY(allowFileAccessFromFileURLs, BOOL)
57
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
62
 RCT_EXPORT_VIEW_PROPERTY(allowsInlineMediaPlayback, BOOL)
58
 RCT_EXPORT_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, BOOL)
63
 RCT_EXPORT_VIEW_PROPERTY(mediaPlaybackRequiresUserAction, BOOL)
61
 #endif
66
 #endif
62
 RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
67
 RCT_EXPORT_VIEW_PROPERTY(contentInset, UIEdgeInsets)
63
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
68
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustContentInsets, BOOL)
69
+RCT_EXPORT_VIEW_PROPERTY(autoManageStatusBarEnabled, BOOL)
64
 RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL)
70
 RCT_EXPORT_VIEW_PROPERTY(hideKeyboardAccessoryView, BOOL)
65
 RCT_EXPORT_VIEW_PROPERTY(allowsBackForwardNavigationGestures, BOOL)
71
 RCT_EXPORT_VIEW_PROPERTY(allowsBackForwardNavigationGestures, BOOL)
66
 RCT_EXPORT_VIEW_PROPERTY(incognito, BOOL)
72
 RCT_EXPORT_VIEW_PROPERTY(incognito, BOOL)
78
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
84
 RCT_EXPORT_VIEW_PROPERTY(automaticallyAdjustsScrollIndicatorInsets, BOOL)
79
 #endif
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
  * Expose methods to enable messaging the webview.
92
  * Expose methods to enable messaging the webview.
83
  */
93
  */
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
 RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWebView) {
114
 RCT_CUSTOM_VIEW_PROPERTY(bounces, BOOL, RNCWebView) {
101
   view.bounces = json == nil ? true : [RCTConvert BOOL: json];
115
   view.bounces = json == nil ? true : [RCTConvert BOOL: json];
102
 }
116
 }
113
     view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json];
127
     view.sharedCookiesEnabled = json == nil ? false : [RCTConvert BOOL: json];
114
 }
128
 }
115
 
129
 
130
+#if !TARGET_OS_OSX
116
 RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWebView) {
131
 RCT_CUSTOM_VIEW_PROPERTY(decelerationRate, CGFloat, RNCWebView) {
117
   view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json];
132
   view.decelerationRate = json == nil ? UIScrollViewDecelerationRateNormal : [RCTConvert CGFloat: json];
118
 }
133
 }
134
+#endif // !TARGET_OS_OSX
119
 
135
 
120
 RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWebView) {
136
 RCT_CUSTOM_VIEW_PROPERTY(directionalLockEnabled, BOOL, RNCWebView) {
121
     view.directionalLockEnabled = json == nil ? true : [RCTConvert BOOL: json];
137
     view.directionalLockEnabled = json == nil ? true : [RCTConvert BOOL: json];

+ 1
- 0
babel.config.js Просмотреть файл

6
         presets: ['module:metro-react-native-babel-preset'],
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
 # React Native installed?
18
 # React Native installed?
19
 if ! [ -x "$(command -v react-native)" ]; then
19
 if ! [ -x "$(command -v react-native)" ]; then
20
   echo 'Error: React Native is not installed.' >&2
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
   echo 'Use the "Building Projects With Native Code" option.'
22
   echo 'Use the "Building Projects With Native Code" option.'
23
   exit 1
23
   exit 1
24
 fi
24
 fi

+ 46
- 0
docs/Contributing.md Просмотреть файл

8
 
8
 
9
 After you fork the repo, clone it to your machine, and make your changes, you'll want to test them in an app.
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
 In a new `react-native init` project, do this:
57
 In a new `react-native init` project, do this:
12
 
58
 
13
 ```
59
 ```

+ 14
- 14
docs/Custom-Android.md Просмотреть файл

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.
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
 ## Native Code
5
 ## Native Code
6
 
6
 
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:
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
 ```java
13
 ```java
14
 @ReactModule(name = CustomWebViewManager.REACT_CLASS)
14
 @ReactModule(name = CustomWebViewManager.REACT_CLASS)
168
 
168
 
169
 To use your custom web view, you'll need to create a class for it. Your class must:
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
 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.
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
 ```javascript
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
 export default class CustomWebView extends Component {
181
 export default class CustomWebView extends Component {
182
   static propTypes = WebView.propTypes;
182
   static propTypes = WebView.propTypes;
183
 
183
 
184
   render() {
184
   render() {
185
     return (
185
     return (
186
-      <WebView {...this.props} nativeConfig={{component: RCTCustomWebView}} />
186
+      <WebView {...this.props} nativeConfig={{ component: RCTCustomWebView }} />
187
     );
187
     );
188
   }
188
   }
189
 }
189
 }
191
 const RCTCustomWebView = requireNativeComponent(
191
 const RCTCustomWebView = requireNativeComponent(
192
   'RCTCustomWebView',
192
   'RCTCustomWebView',
193
   CustomWebView,
193
   CustomWebView,
194
-  WebView.extraNativeComponentConfig
194
+  WebView.extraNativeComponentConfig,
195
 );
195
 );
196
 ```
196
 ```
197
 
197
 
213
     finalUrl: 'about:blank',
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
     onNavigationCompleted && onNavigationCompleted(event);
218
     onNavigationCompleted && onNavigationCompleted(event);
219
   };
219
   };
220
 
220
 
249
       ...WebView.extraNativeComponentConfig.nativeOnly,
249
       ...WebView.extraNativeComponentConfig.nativeOnly,
250
       onScrollToBottom: true,
250
       onScrollToBottom: true,
251
     },
251
     },
252
-  }
252
+  },
253
 );
253
 );
254
 ```
254
 ```

+ 3
- 1
docs/Debugging.md Просмотреть файл

15
 3. Safari -> Develop -> [device name] -> [app name] -> [url - title]
15
 3. Safari -> Develop -> [device name] -> [app name] -> [url - title]
16
 4. You can now debug the WebView contents just as you would on the web
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
 When debugging on device you must enable Web Inspector in your device settings:
20
 When debugging on device you must enable Web Inspector in your device settings:
21
 
21
 
22
 Settings -> Safari -> Advanced -> Web Inspector
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
 ### Android & Chrome
26
 ### Android & Chrome
25
 
27
 
26
 It's possible to debug WebView contents in the Android emulator or on a device using Chrome DevTools.
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
 
2
 
3
 Here's how to get started quickly with the React Native WebView.
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
 $ yarn add react-native-webview
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
 $ npm install --save react-native-webview
16
 $ npm install --save react-native-webview
15
 ```
17
 ```
16
 
18
 
17
-#### 2. Link native dependencies
19
+## 2. Link native dependencies
18
 
20
 
19
 From react-native 0.60 autolinking will take care of the link step but don't forget to run `pod install`
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
 $ react-native link react-native-webview
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
 $ pod install
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
 Android - react-native-webview version <6:
44
 Android - react-native-webview version <6:
35
 This module does not require any extra step after running the link command 🎉
45
 This module does not require any extra step after running the link command 🎉
36
 
46
 
44
 
54
 
45
 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.
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
 ```js
88
 ```js
55
 import React, { Component } from 'react';
89
 import React, { Component } from 'react';

+ 69
- 67
docs/Guide.md Просмотреть файл

48
 
48
 
49
 class MyWeb extends Component {
49
 class MyWeb extends Component {
50
   render() {
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
 ### Loading local HTML files
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
 ```js
64
 ```js
63
 import React, { Component } from 'react';
65
 import React, { Component } from 'react';
64
 import { WebView } from 'react-native-webview';
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
 class MyWeb extends Component {
70
 class MyWeb extends Component {
69
   render() {
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
 ```js
79
 ```js
80
 import React, { Component } from 'react';
80
 import React, { Component } from 'react';
83
 class MyWeb extends Component {
83
 class MyWeb extends Component {
84
   render() {
84
   render() {
85
     return (
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
 ### Controlling navigation state changes
94
 ### Controlling navigation state changes
93
 
95
 
94
 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.
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
   render() {
105
   render() {
104
     return (
106
     return (
105
       <WebView
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
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
110
         onNavigationStateChange={this.handleWebViewNavigationStateChange}
109
       />
111
       />
110
     );
112
     );
111
   }
113
   }
112
 
114
 
113
-  handleWebViewNavigationStateChange = newNavState => {
115
+  handleWebViewNavigationStateChange = (newNavState) => {
114
     // newNavState looks something like this:
116
     // newNavState looks something like this:
115
     // {
117
     // {
116
     //   url?: string;
118
     //   url?: string;
141
 
143
 
142
     // redirect somewhere else
144
     // redirect somewhere else
143
     if (url.includes('google.com')) {
145
     if (url.includes('google.com')) {
144
-      const newURL = 'https://facebook.github.io/react-native/';
146
+      const newURL = 'https://reactnative.dev/';
145
       const redirectTo = 'window.location = "' + newURL + '"';
147
       const redirectTo = 'window.location = "' + newURL + '"';
146
       this.webview.injectJavaScript(redirectTo);
148
       this.webview.injectJavaScript(redirectTo);
147
     }
149
     }
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
 ### Add support for File Upload
154
 ### Add support for File Upload
191
 
155
 
192
 ##### iOS
156
 ##### iOS
229
 </manifest>
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
 ##### Check for File Upload support, with `static isFileUploadSupported()`
202
 ##### Check for File Upload support, with `static isFileUploadSupported()`
233
 
203
 
234
 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)):
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
 
232
 
263
 ##### iOS
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
 <key>NSPhotoLibraryAddUsageDescription</key>
255
 <key>NSPhotoLibraryAddUsageDescription</key>
273
 
258
 
274
 ##### Android
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
 ```xml
264
 ```xml
279
 <manifest ...>
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
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
269
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
284
 
270
 
285
   ......
271
   ......
319
             uri:
305
             uri:
320
               'https://github.com/react-native-community/react-native-webview',
306
               'https://github.com/react-native-community/react-native-webview',
321
           }}
307
           }}
308
+          onMessage={(event) => {}}
322
           injectedJavaScript={runFirst}
309
           injectedJavaScript={runFirst}
323
         />
310
         />
324
       </View>
311
       </View>
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
 <img alt="screenshot of Github repo" width="200" src="https://user-images.githubusercontent.com/1479215/53609254-e5dc9c00-3b7a-11e9-9118-bc4e520ce6ca.png" />
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
 _Under the hood_
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
 > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
326
 > On Android, `injectedJavaScript` runs a method on the Android WebView called `evaluateJavascriptWithFallback`
338
 
327
 
339
-
340
 #### The `injectedJavaScriptBeforeContentLoaded` prop
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
 ```jsx
332
 ```jsx
345
 import React, { Component } from 'react';
333
 import React, { Component } from 'react';
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
 #### The `injectJavaScript` method
366
 #### The `injectJavaScript` method
373
 
367
 
392
     return (
386
     return (
393
       <View style={{ flex: 1 }}>
387
       <View style={{ flex: 1 }}>
394
         <WebView
388
         <WebView
395
-          ref={r => (this.webref = r)}
389
+          ref={(r) => (this.webref = r)}
396
           source={{
390
           source={{
397
             uri:
391
             uri:
398
               'https://github.com/react-native-community/react-native-webview',
392
               'https://github.com/react-native-community/react-native-webview',
445
       <View style={{ flex: 1 }}>
439
       <View style={{ flex: 1 }}>
446
         <WebView
440
         <WebView
447
           source={{ html }}
441
           source={{ html }}
448
-          onMessage={event => {
442
+          onMessage={(event) => {
449
             alert(event.nativeEvent.data);
443
             alert(event.nativeEvent.data);
450
           }}
444
           }}
451
         />
445
         />
481
 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)):
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
 ```jsx
477
 ```jsx
484
-const CustomHeaderWebView = props => {
478
+const CustomHeaderWebView = (props) => {
485
   const { uri, onLoadStart, ...restProps } = props;
479
   const { uri, onLoadStart, ...restProps } = props;
486
   const [currentURI, setURI] = useState(props.source.uri);
480
   const [currentURI, setURI] = useState(props.source.uri);
487
   const newSource = { ...props.source, uri: currentURI };
481
   const newSource = { ...props.source, uri: currentURI };
490
     <WebView
484
     <WebView
491
       {...restProps}
485
       {...restProps}
492
       source={newSource}
486
       source={newSource}
493
-      onShouldStartLoadWithRequest={request => {
487
+      onShouldStartLoadWithRequest={(request) => {
494
         // If we're loading the current URI, allow it to load
488
         // If we're loading the current URI, allow it to load
495
         if (request.url === currentURI) return true;
489
         if (request.url === currentURI) return true;
496
         // We're loading a new URL -- change state first
490
         // We're loading a new URL -- change state first
513
 
507
 
514
 #### Managing Cookies
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
 ```jsx
514
 ```jsx
521
 const App = () => {
515
 const App = () => {
547
 ```
541
 ```
548
 
542
 
549
 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).
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
 
19
 
20
 - [x] iOS
20
 - [x] iOS
21
 - [x] Android
21
 - [x] Android
22
+- [x] macOS
23
+- [x] Windows
22
 
24
 
23
 _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)._
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
 
36
 
35
 Versão atual: ![version](https://img.shields.io/npm/v/react-native-webview.svg)
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
 - [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.
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
 - [4.0.0](https://github.com/react-native-community/react-native-webview/releases/tag/v4.0.0) - Cache adicionada(habilitada por padrão).
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
 // ...
66
 // ...
63
 class MyWebComponent extends Component {
67
 class MyWebComponent extends Component {
64
   render() {
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 Просмотреть файл

1
+*.pbxproj -text

+ 67
- 0
example/.gitignore Просмотреть файл

1
+# OSX
2
+#
3
+.DS_Store
4
+
5
+# Xcode
6
+#
7
+build/
8
+*.pbxuser
9
+!default.pbxuser
10
+*.mode1v3
11
+!default.mode1v3
12
+*.mode2v3
13
+!default.mode2v3
14
+*.perspectivev3
15
+!default.perspectivev3
16
+xcuserdata
17
+*.xccheckout
18
+*.moved-aside
19
+DerivedData
20
+*.hmap
21
+*.ipa
22
+*.xcuserstate
23
+# 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 Просмотреть файл

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

+ 1
- 0
example/.watchmanconfig Просмотреть файл

1
+{}

+ 216
- 0
example/App.tsx Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

1
+<resources>
2
+    <string name="app_name">example</string>
3
+</resources>

+ 9
- 0
example/android/app/src/main/res/values/styles.xml Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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

+ 9
- 0
example/assets/test.html Просмотреть файл

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 Просмотреть файл

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

+ 72
- 0
example/examples/Alerts.tsx Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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

+ 57
- 0
example/ios/example/Info.plist Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл

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 Просмотреть файл


Некоторые файлы не были показаны из-за большого количества измененных файлов