diff --git a/.gitignore b/.gitignore index f9e8706e..d477ec05 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,13 @@ local.properties # node.js # node_modules/ -npm-debug.log +*.log + +# yarn +yarn.lock + +# editor workspace settings +.vscode # BUCK buck-out/ diff --git a/.npmignore b/.npmignore index 6f30a3ab..46b411bf 100644 --- a/.npmignore +++ b/.npmignore @@ -1 +1 @@ -/example +/examples diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fe396f5..ad1894ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Next Version * Partial support for timed metadata on Android MediaPlayer [#707](https://github.com/react-native-community/react-native-video/pull/707) * Update to ExoPlayer 2.8.2. Android SDK 26 now required [#1170](https://github.com/react-native-community/react-native-video/pull/1170) +* Support video caching for iOS [#955](https://github.com/react-native-community/react-native-video/pull/955) ### Version 3.2.0 * Basic fullscreen support for Android MediaPlayer [#1138](https://github.com/react-native-community/react-native-video/pull/1138) diff --git a/README.md b/README.md index 0f1717ef..4d15b336 100644 --- a/README.md +++ b/README.md @@ -11,10 +11,13 @@ Version 4.0.0 now requires Android SDK 26 or higher to use ExoPlayer. This is th ### Version 3.0.0 breaking changes Version 3.0 features a number of changes to existing behavior. See [Updating](#updating) for changes. -## TOC +## Table of Contents * [Installation](#installation) * [Usage](#usage) +* [iOS App Transport Security](#ios-app-transport-security) +* [Audio Mixing](#audio-mixing) +* [Android Expansion File Usage](#android-expansion-file-usage) * [Updating](#updating) ## Installation @@ -31,26 +34,37 @@ or using yarn: yarn add react-native-video ``` +Then follow the instructions for your platform to link react-native-video into your project: +
iOS +### Standard Method + Run `react-native link react-native-video` to link the react-native-video library. -If you would like to allow other apps to play music over your video component, add: +### Using CocoaPods (required to enable caching) -**AppDelegate.m** +Setup your Podfile like it is described in the [react-native documentation](https://facebook.github.io/react-native/docs/integration-with-existing-apps#configuring-cocoapods-dependencies). -```objective-c -#import // import +Depending on your requirements you have to choose between the two possible subpodspecs: -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - ... - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; // allow - ... -} +Video only: + +```diff + pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' ++ `pod 'react-native-video', :path => '../node_modules/react-native-video/react-native-video.podspec'` +end ``` -Note: you can also use the `ignoreSilentSwitch` prop, shown below. + +Video with caching ([more info](docs/caching.md)): + +```diff + pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec' ++ `pod 'react-native-video/VideoCaching', :path => '../node_modules/react-native-video/react-native-video.podspec'` +end +``` +
@@ -716,17 +730,37 @@ this.player.seek(120, 50); // Seek to 2 minutes with +/- 50 milliseconds accurac Platforms: iOS -### Additional props +### iOS App Transport Security -To see the full list of available props, you can check the [propTypes](https://github.com/react-native-community/react-native-video/blob/master/Video.js#L246) of the Video.js component. - -- By default, iOS 9+ will only load encrypted HTTPS urls. If you need to load content from a webserver that only supports HTTP, you will need to modify your Info.plist file and add the following entry: +- By default, iOS will only load encrypted (https) urls. If you want to load content from an unencrypted (http) source, you will need to modify your Info.plist file and add the following entry: For more detailed info check this [article](https://cocoacasts.com/how-to-add-app-transport-security-exception-domains)
+### Audio Mixing + +At some point in the future, react-native-video will include an Audio Manager for configuring how videos mix with other apps playing sounds on the device. + +On iOS, if you would like to allow other apps to play music over your video component, make the following change: + +**AppDelegate.m** + +```objective-c +#import // import + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + ... + [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; // allow + ... +} +``` + +You can also use the [ignoreSilentSwitch](ignoresilentswitch) prop. + + ### Android Expansion File Usage Expansions files allow you to ship assets that exceed the 100MB apk size limit and don't need to be updated each time you push an app update. diff --git a/docs/caching.md b/docs/caching.md new file mode 100644 index 00000000..c2b8df97 --- /dev/null +++ b/docs/caching.md @@ -0,0 +1,22 @@ +# Caching + +Caching is currently only supported on `iOS` platforms with a CocoaPods setup. + +# Technology + +The cache is backed by [SPTPersistentCache](https://github.com/spotify/SPTPersistentCache) and [DVAssetLoaderDelegate](https://github.com/vdugnist/DVAssetLoaderDelegate). + +# How Does It Work + +The caching is based on the url of the asset. +SPTPersistentCache is a LRU ([Least Recently Used](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU))) cache. + +# Restrictions + +Currently, caching is only supported for URLs that end in a `.mp4`, `.m4v`, or `.mov` extension. In future versions, URLs that end in a query string (e.g. test.mp4?resolution=480p) will be support once dependencies allow access to the `Content-Type` header. At this time, HLS playlists (.m3u8) and videos that sideload text tracks are not supported and will bypass the cache. + +You will also receive warnings in the Xcode logs by using the `debug` mode. So if you are not 100% sure if your video is cached, check your Xcode logs! + +By default files expire after 30 days and the maxmimum cache size is 100mb. + +In a future release the cache might have more configurable options. diff --git a/example/package.json b/example/package.json deleted file mode 100644 index 753ea318..00000000 --- a/example/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "VideoPlayer", - "version": "1.0.0", - "private": true, - "scripts": { - "start": "node_modules/react-native/packager/packager.sh" - }, - "dependencies": { - "react": "15.4.2", - "react-native": "^0.42.0", - "react-native-video": "file:../", - "react-native-windows": "^0.40.0" - }, - "devDependencies": { - "rnpm-plugin-windows": "~0.2.3" - } -} diff --git a/example/.babelrc b/examples/basic/.babelrc similarity index 100% rename from example/.babelrc rename to examples/basic/.babelrc diff --git a/example/.buckconfig b/examples/basic/.buckconfig similarity index 100% rename from example/.buckconfig rename to examples/basic/.buckconfig diff --git a/example/.flowconfig b/examples/basic/.flowconfig similarity index 100% rename from example/.flowconfig rename to examples/basic/.flowconfig diff --git a/example/.gitattributes b/examples/basic/.gitattributes similarity index 100% rename from example/.gitattributes rename to examples/basic/.gitattributes diff --git a/example/.gitignore b/examples/basic/.gitignore similarity index 100% rename from example/.gitignore rename to examples/basic/.gitignore diff --git a/example/.watchmanconfig b/examples/basic/.watchmanconfig similarity index 100% rename from example/.watchmanconfig rename to examples/basic/.watchmanconfig diff --git a/example/android/app/BUCK b/examples/basic/android/app/BUCK similarity index 100% rename from example/android/app/BUCK rename to examples/basic/android/app/BUCK diff --git a/example/android/app/build.gradle b/examples/basic/android/app/build.gradle similarity index 100% rename from example/android/app/build.gradle rename to examples/basic/android/app/build.gradle diff --git a/example/android/app/proguard-rules.pro b/examples/basic/android/app/proguard-rules.pro similarity index 100% rename from example/android/app/proguard-rules.pro rename to examples/basic/android/app/proguard-rules.pro diff --git a/example/android/app/src/main/AndroidManifest.xml b/examples/basic/android/app/src/main/AndroidManifest.xml similarity index 100% rename from example/android/app/src/main/AndroidManifest.xml rename to examples/basic/android/app/src/main/AndroidManifest.xml diff --git a/example/android/app/src/main/java/com/videoplayer/MainActivity.java b/examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java similarity index 100% rename from example/android/app/src/main/java/com/videoplayer/MainActivity.java rename to examples/basic/android/app/src/main/java/com/videoplayer/MainActivity.java diff --git a/example/android/app/src/main/java/com/videoplayer/MainApplication.java b/examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java similarity index 100% rename from example/android/app/src/main/java/com/videoplayer/MainApplication.java rename to examples/basic/android/app/src/main/java/com/videoplayer/MainApplication.java diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to examples/basic/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to examples/basic/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to examples/basic/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to examples/basic/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values/strings.xml b/examples/basic/android/app/src/main/res/values/strings.xml similarity index 100% rename from example/android/app/src/main/res/values/strings.xml rename to examples/basic/android/app/src/main/res/values/strings.xml diff --git a/example/android/app/src/main/res/values/styles.xml b/examples/basic/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to examples/basic/android/app/src/main/res/values/styles.xml diff --git a/example/android/build.gradle b/examples/basic/android/build.gradle similarity index 100% rename from example/android/build.gradle rename to examples/basic/android/build.gradle diff --git a/example/android/gradle.properties b/examples/basic/android/gradle.properties similarity index 100% rename from example/android/gradle.properties rename to examples/basic/android/gradle.properties diff --git a/example/android/gradle/wrapper/gradle-wrapper.jar b/examples/basic/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.jar rename to examples/basic/android/gradle/wrapper/gradle-wrapper.jar diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/examples/basic/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to examples/basic/android/gradle/wrapper/gradle-wrapper.properties diff --git a/example/android/gradlew b/examples/basic/android/gradlew similarity index 100% rename from example/android/gradlew rename to examples/basic/android/gradlew diff --git a/example/android/gradlew.bat b/examples/basic/android/gradlew.bat similarity index 100% rename from example/android/gradlew.bat rename to examples/basic/android/gradlew.bat diff --git a/example/android/keystores/BUCK b/examples/basic/android/keystores/BUCK similarity index 100% rename from example/android/keystores/BUCK rename to examples/basic/android/keystores/BUCK diff --git a/example/android/keystores/debug.keystore.properties b/examples/basic/android/keystores/debug.keystore.properties similarity index 100% rename from example/android/keystores/debug.keystore.properties rename to examples/basic/android/keystores/debug.keystore.properties diff --git a/example/android/settings.gradle b/examples/basic/android/settings.gradle similarity index 100% rename from example/android/settings.gradle rename to examples/basic/android/settings.gradle diff --git a/example/broadchurch.mp4 b/examples/basic/broadchurch.mp4 similarity index 100% rename from example/broadchurch.mp4 rename to examples/basic/broadchurch.mp4 diff --git a/example/index.android.js b/examples/basic/index.android.js similarity index 100% rename from example/index.android.js rename to examples/basic/index.android.js diff --git a/example/index.ios.js b/examples/basic/index.ios.js similarity index 100% rename from example/index.ios.js rename to examples/basic/index.ios.js diff --git a/example/index.windows.js b/examples/basic/index.windows.js similarity index 100% rename from example/index.windows.js rename to examples/basic/index.windows.js diff --git a/example/ios/AppDelegate.h b/examples/basic/ios/AppDelegate.h similarity index 100% rename from example/ios/AppDelegate.h rename to examples/basic/ios/AppDelegate.h diff --git a/example/ios/AppDelegate.m b/examples/basic/ios/AppDelegate.m similarity index 100% rename from example/ios/AppDelegate.m rename to examples/basic/ios/AppDelegate.m diff --git a/example/ios/Base.lproj/LaunchScreen.xib b/examples/basic/ios/Base.lproj/LaunchScreen.xib similarity index 100% rename from example/ios/Base.lproj/LaunchScreen.xib rename to examples/basic/ios/Base.lproj/LaunchScreen.xib diff --git a/example/ios/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/Images.xcassets/AppIcon.appiconset/Contents.json rename to examples/basic/ios/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/Info.plist b/examples/basic/ios/Info.plist similarity index 100% rename from example/ios/Info.plist rename to examples/basic/ios/Info.plist diff --git a/example/ios/VideoPlayer.xcodeproj/project.pbxproj b/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj similarity index 86% rename from example/ios/VideoPlayer.xcodeproj/project.pbxproj rename to examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj index 98ec595e..4c3fa6f2 100644 --- a/example/ios/VideoPlayer.xcodeproj/project.pbxproj +++ b/examples/basic/ios/VideoPlayer.xcodeproj/project.pbxproj @@ -217,6 +217,83 @@ remoteGlobalIDString = 134814201AA4EA6300B7C361; remoteInfo = RCTVideo; }; + D1107C532111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 8C2A0F651E25608300E31596 /* RCTVideo.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 641E28441F0EEC8500443AF6; + remoteInfo = "RCTVideo-tvOS"; + }; + D1107C592111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D001F3B181A0099AA32; + remoteInfo = fishhook; + }; + D1107C5B2111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; + remoteInfo = "fishhook-tvOS"; + }; + D1107C6D2111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BDC1FC498900052F4D5; + remoteInfo = jsinspector; + }; + D1107C6F2111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5; + remoteInfo = "jsinspector-tvOS"; + }; + D1107C712111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; + remoteInfo = "third-party"; + }; + D1107C732111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; + remoteInfo = "third-party-tvOS"; + }; + D1107C752111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7E881E25C6D100323FB7; + remoteInfo = "double-conversion"; + }; + D1107C772111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D621EBD27B9005632C8; + remoteInfo = "double-conversion-tvOS"; + }; + D1107C792111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; + remoteInfo = privatedata; + }; + D1107C7B2111145500073188 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; + remoteInfo = "privatedata-tvOS"; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ @@ -349,6 +426,8 @@ children = ( 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, + D1107C5A2111145500073188 /* libfishhook.a */, + D1107C5C2111145500073188 /* libfishhook-tvOS.a */, ); name = Products; sourceTree = ""; @@ -378,6 +457,14 @@ 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, + D1107C6E2111145500073188 /* libjsinspector.a */, + D1107C702111145500073188 /* libjsinspector-tvOS.a */, + D1107C722111145500073188 /* libthird-party.a */, + D1107C742111145500073188 /* libthird-party.a */, + D1107C762111145500073188 /* libdouble-conversion.a */, + D1107C782111145500073188 /* libdouble-conversion.a */, + D1107C7A2111145500073188 /* libprivatedata.a */, + D1107C7C2111145500073188 /* libprivatedata-tvOS.a */, ); name = Products; sourceTree = ""; @@ -453,6 +540,7 @@ isa = PBXGroup; children = ( 8C2A0F791E25608300E31596 /* libRCTVideo.a */, + D1107C542111145500073188 /* libRCTVideo.a */, ); name = Products; sourceTree = ""; @@ -738,7 +826,7 @@ 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation-tvOS.a */ = { isa = PBXReferenceProxy; fileType = archive.ar; - path = "libRCTAnimation-tvOS.a"; + path = libRCTAnimation.a; remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -763,6 +851,83 @@ remoteRef = 8C2A0F781E25608300E31596 /* PBXContainerItemProxy */; sourceTree = BUILT_PRODUCTS_DIR; }; + D1107C542111145500073188 /* libRCTVideo.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTVideo.a; + remoteRef = D1107C532111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C5A2111145500073188 /* libfishhook.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libfishhook.a; + remoteRef = D1107C592111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C5C2111145500073188 /* libfishhook-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libfishhook-tvOS.a"; + remoteRef = D1107C5B2111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C6E2111145500073188 /* libjsinspector.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjsinspector.a; + remoteRef = D1107C6D2111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C702111145500073188 /* libjsinspector-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libjsinspector-tvOS.a"; + remoteRef = D1107C6F2111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C722111145500073188 /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = D1107C712111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C742111145500073188 /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = D1107C732111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C762111145500073188 /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = D1107C752111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C782111145500073188 /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = D1107C772111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C7A2111145500073188 /* libprivatedata.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libprivatedata.a; + remoteRef = D1107C792111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + D1107C7C2111145500073188 /* libprivatedata-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libprivatedata-tvOS.a"; + remoteRef = D1107C7B2111145500073188 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; /* End PBXReferenceProxy section */ /* Begin PBXResourcesBuildPhase section */ @@ -797,7 +962,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh"; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; }; /* End PBXShellScriptBuildPhase section */ diff --git a/example/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme b/examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme similarity index 100% rename from example/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme rename to examples/basic/ios/VideoPlayer.xcodeproj/xcshareddata/xcschemes/VideoPlayer.xcscheme diff --git a/example/ios/VideoPlayer/AppDelegate.h b/examples/basic/ios/VideoPlayer/AppDelegate.h similarity index 100% rename from example/ios/VideoPlayer/AppDelegate.h rename to examples/basic/ios/VideoPlayer/AppDelegate.h diff --git a/example/ios/VideoPlayer/AppDelegate.m b/examples/basic/ios/VideoPlayer/AppDelegate.m similarity index 100% rename from example/ios/VideoPlayer/AppDelegate.m rename to examples/basic/ios/VideoPlayer/AppDelegate.m diff --git a/example/ios/VideoPlayer/Base.lproj/LaunchScreen.xib b/examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib similarity index 100% rename from example/ios/VideoPlayer/Base.lproj/LaunchScreen.xib rename to examples/basic/ios/VideoPlayer/Base.lproj/LaunchScreen.xib diff --git a/example/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/basic/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json rename to examples/basic/ios/VideoPlayer/Images.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/ios/VideoPlayer/Info.plist b/examples/basic/ios/VideoPlayer/Info.plist similarity index 100% rename from example/ios/VideoPlayer/Info.plist rename to examples/basic/ios/VideoPlayer/Info.plist diff --git a/example/ios/VideoPlayer/main.m b/examples/basic/ios/VideoPlayer/main.m similarity index 100% rename from example/ios/VideoPlayer/main.m rename to examples/basic/ios/VideoPlayer/main.m diff --git a/example/ios/VideoPlayerTests/Info.plist b/examples/basic/ios/VideoPlayerTests/Info.plist similarity index 100% rename from example/ios/VideoPlayerTests/Info.plist rename to examples/basic/ios/VideoPlayerTests/Info.plist diff --git a/example/ios/VideoPlayerTests/VideoPlayerTests.m b/examples/basic/ios/VideoPlayerTests/VideoPlayerTests.m similarity index 100% rename from example/ios/VideoPlayerTests/VideoPlayerTests.m rename to examples/basic/ios/VideoPlayerTests/VideoPlayerTests.m diff --git a/example/ios/main.jsbundle b/examples/basic/ios/main.jsbundle similarity index 100% rename from example/ios/main.jsbundle rename to examples/basic/ios/main.jsbundle diff --git a/example/ios/main.m b/examples/basic/ios/main.m similarity index 100% rename from example/ios/main.m rename to examples/basic/ios/main.m diff --git a/examples/basic/package.json b/examples/basic/package.json new file mode 100644 index 00000000..c617dd26 --- /dev/null +++ b/examples/basic/package.json @@ -0,0 +1,21 @@ +{ + "name": "VideoPlayer", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "node node_modules/react-native/local-cli/cli.js start", + "test": "jest" + }, + "dependencies": { + "react": "16.4.1", + "react-native": "0.56.0", + "react-native-video": "file:../.." + }, + "devDependencies": { + "babel-jest": "22.4.1", + "babel-preset-react-native": "5.0.2", + "express": "^4.16.2", + "jest": "22.4.2", + "react-test-renderer": "16.2.0" + } +} diff --git a/examples/basic/rn-cli.config.js b/examples/basic/rn-cli.config.js new file mode 100644 index 00000000..1b84294b --- /dev/null +++ b/examples/basic/rn-cli.config.js @@ -0,0 +1,7 @@ +const blacklist = require('metro').createBlacklist; + +module.exports = { + getBlacklistRE: function() { + return blacklist([/node_modules\/react-native-video\/examples\/.*/]); + } +}; diff --git a/example/windows/.gitignore b/examples/basic/windows/.gitignore similarity index 100% rename from example/windows/.gitignore rename to examples/basic/windows/.gitignore diff --git a/example/windows/VideoPlayer.sln b/examples/basic/windows/VideoPlayer.sln similarity index 100% rename from example/windows/VideoPlayer.sln rename to examples/basic/windows/VideoPlayer.sln diff --git a/example/windows/VideoPlayer/App.xaml b/examples/basic/windows/VideoPlayer/App.xaml similarity index 100% rename from example/windows/VideoPlayer/App.xaml rename to examples/basic/windows/VideoPlayer/App.xaml diff --git a/example/windows/VideoPlayer/App.xaml.cs b/examples/basic/windows/VideoPlayer/App.xaml.cs similarity index 100% rename from example/windows/VideoPlayer/App.xaml.cs rename to examples/basic/windows/VideoPlayer/App.xaml.cs diff --git a/example/windows/VideoPlayer/Assets/LockScreenLogo.scale-200.png b/examples/basic/windows/VideoPlayer/Assets/LockScreenLogo.scale-200.png similarity index 100% rename from example/windows/VideoPlayer/Assets/LockScreenLogo.scale-200.png rename to examples/basic/windows/VideoPlayer/Assets/LockScreenLogo.scale-200.png diff --git a/example/windows/VideoPlayer/Assets/SplashScreen.scale-200.png b/examples/basic/windows/VideoPlayer/Assets/SplashScreen.scale-200.png similarity index 100% rename from example/windows/VideoPlayer/Assets/SplashScreen.scale-200.png rename to examples/basic/windows/VideoPlayer/Assets/SplashScreen.scale-200.png diff --git a/example/windows/VideoPlayer/Assets/Square150x150Logo.scale-200.png b/examples/basic/windows/VideoPlayer/Assets/Square150x150Logo.scale-200.png similarity index 100% rename from example/windows/VideoPlayer/Assets/Square150x150Logo.scale-200.png rename to examples/basic/windows/VideoPlayer/Assets/Square150x150Logo.scale-200.png diff --git a/example/windows/VideoPlayer/Assets/Square44x44Logo.scale-200.png b/examples/basic/windows/VideoPlayer/Assets/Square44x44Logo.scale-200.png similarity index 100% rename from example/windows/VideoPlayer/Assets/Square44x44Logo.scale-200.png rename to examples/basic/windows/VideoPlayer/Assets/Square44x44Logo.scale-200.png diff --git a/example/windows/VideoPlayer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png b/examples/basic/windows/VideoPlayer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png similarity index 100% rename from example/windows/VideoPlayer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png rename to examples/basic/windows/VideoPlayer/Assets/Square44x44Logo.targetsize-24_altform-unplated.png diff --git a/example/windows/VideoPlayer/Assets/StoreLogo.png b/examples/basic/windows/VideoPlayer/Assets/StoreLogo.png similarity index 100% rename from example/windows/VideoPlayer/Assets/StoreLogo.png rename to examples/basic/windows/VideoPlayer/Assets/StoreLogo.png diff --git a/example/windows/VideoPlayer/Assets/Wide310x150Logo.scale-200.png b/examples/basic/windows/VideoPlayer/Assets/Wide310x150Logo.scale-200.png similarity index 100% rename from example/windows/VideoPlayer/Assets/Wide310x150Logo.scale-200.png rename to examples/basic/windows/VideoPlayer/Assets/Wide310x150Logo.scale-200.png diff --git a/example/windows/VideoPlayer/MainPage.cs b/examples/basic/windows/VideoPlayer/MainPage.cs similarity index 100% rename from example/windows/VideoPlayer/MainPage.cs rename to examples/basic/windows/VideoPlayer/MainPage.cs diff --git a/example/windows/VideoPlayer/Package.appxmanifest b/examples/basic/windows/VideoPlayer/Package.appxmanifest similarity index 100% rename from example/windows/VideoPlayer/Package.appxmanifest rename to examples/basic/windows/VideoPlayer/Package.appxmanifest diff --git a/example/windows/VideoPlayer/Properties/AssemblyInfo.cs b/examples/basic/windows/VideoPlayer/Properties/AssemblyInfo.cs similarity index 100% rename from example/windows/VideoPlayer/Properties/AssemblyInfo.cs rename to examples/basic/windows/VideoPlayer/Properties/AssemblyInfo.cs diff --git a/example/windows/VideoPlayer/Properties/Default.rd.xml b/examples/basic/windows/VideoPlayer/Properties/Default.rd.xml similarity index 100% rename from example/windows/VideoPlayer/Properties/Default.rd.xml rename to examples/basic/windows/VideoPlayer/Properties/Default.rd.xml diff --git a/example/windows/VideoPlayer/VideoPlayer.csproj b/examples/basic/windows/VideoPlayer/VideoPlayer.csproj similarity index 100% rename from example/windows/VideoPlayer/VideoPlayer.csproj rename to examples/basic/windows/VideoPlayer/VideoPlayer.csproj diff --git a/example/windows/VideoPlayer/VideoPlayer_TemporaryKey.pfx b/examples/basic/windows/VideoPlayer/VideoPlayer_TemporaryKey.pfx similarity index 100% rename from example/windows/VideoPlayer/VideoPlayer_TemporaryKey.pfx rename to examples/basic/windows/VideoPlayer/VideoPlayer_TemporaryKey.pfx diff --git a/example/windows/VideoPlayer/project.json b/examples/basic/windows/VideoPlayer/project.json similarity index 100% rename from example/windows/VideoPlayer/project.json rename to examples/basic/windows/VideoPlayer/project.json diff --git a/example/yarn.lock b/examples/basic/yarn.lock similarity index 100% rename from example/yarn.lock rename to examples/basic/yarn.lock diff --git a/examples/video-caching/.babelrc b/examples/video-caching/.babelrc new file mode 100644 index 00000000..a9ce1369 --- /dev/null +++ b/examples/video-caching/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["react-native"] +} diff --git a/examples/video-caching/.buckconfig b/examples/video-caching/.buckconfig new file mode 100644 index 00000000..934256cb --- /dev/null +++ b/examples/video-caching/.buckconfig @@ -0,0 +1,6 @@ + +[android] + target = Google Inc.:Google APIs:23 + +[maven_repositories] + central = https://repo1.maven.org/maven2 diff --git a/examples/video-caching/.flowconfig b/examples/video-caching/.flowconfig new file mode 100644 index 00000000..0b611f07 --- /dev/null +++ b/examples/video-caching/.flowconfig @@ -0,0 +1,54 @@ +[ignore] +; We fork some components by platform +.*/*[.]android.js + +; Ignore "BUCK" generated dirs +/\.buckd/ + +; Ignore unexpected extra "@providesModule" +.*/node_modules/.*/node_modules/fbjs/.* + +; Ignore duplicate module providers +; For RN Apps installed via npm, "Libraries" folder is inside +; "node_modules/react-native" but in the source repo it is in the root +.*/Libraries/react-native/React.js + +; Ignore polyfills +.*/Libraries/polyfills/.* + +; Ignore metro +.*/node_modules/metro/.* + +[include] + +[libs] +node_modules/react-native/Libraries/react-native/react-native-interface.js +node_modules/react-native/flow/ +node_modules/react-native/flow-github/ + +[options] +emoji=true + +module.system=haste + +munge_underscores=true + +module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' + +module.file_ext=.js +module.file_ext=.jsx +module.file_ext=.json +module.file_ext=.native.js + +suppress_type=$FlowIssue +suppress_type=$FlowFixMe +suppress_type=$FlowFixMeProps +suppress_type=$FlowFixMeState + +suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) +suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ +suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy +suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError + +[version] +^0.63.0 diff --git a/examples/video-caching/.gitattributes b/examples/video-caching/.gitattributes new file mode 100644 index 00000000..d42ff183 --- /dev/null +++ b/examples/video-caching/.gitattributes @@ -0,0 +1 @@ +*.pbxproj -text diff --git a/examples/video-caching/.gitignore b/examples/video-caching/.gitignore new file mode 100644 index 00000000..2c0f9daa --- /dev/null +++ b/examples/video-caching/.gitignore @@ -0,0 +1,57 @@ +# OSX +# +.DS_Store + +# Xcode +# +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserstate +project.xcworkspace + +# CocoaPods +ios/Pods +ios/*.xcworkspace + +# Android/IntelliJ +# +build/ +.idea +.gradle +local.properties +*.iml + +# node.js +# +node_modules/ +npm-debug.log +yarn-error.log + +# BUCK +buck-out/ +\.buckd/ +*.keystore + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/ + +*/fastlane/report.xml +*/fastlane/Preview.html +*/fastlane/screenshots diff --git a/examples/video-caching/.watchmanconfig b/examples/video-caching/.watchmanconfig new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/examples/video-caching/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/examples/video-caching/App.ios.js b/examples/video-caching/App.ios.js new file mode 100644 index 00000000..e214cfc1 --- /dev/null +++ b/examples/video-caching/App.ios.js @@ -0,0 +1,48 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * @flow + */ + +import React, { Component } from "react"; +import { StyleSheet, Text, View, Dimensions } from "react-native"; +import Video from "react-native-video"; + +const { height, width } = Dimensions.get("screen"); + +type Props = {}; +export default class App extends Component { + render() { + return ( + + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "#F5FCFF" + }, + welcome: { + fontSize: 20, + textAlign: "center", + margin: 10 + } +}); diff --git a/examples/video-caching/App.js b/examples/video-caching/App.js new file mode 100644 index 00000000..a19f4d5d --- /dev/null +++ b/examples/video-caching/App.js @@ -0,0 +1,33 @@ +/** + * Sample React Native App + * https://github.com/facebook/react-native + * @flow + */ + +import React, { Component } from "react"; +import { StyleSheet, Text, View } from "react-native"; + +type Props = {}; +export default class App extends Component { + render() { + return ( + + Caching is only supported in iOS! + + ); + } +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "#F5FCFF" + }, + welcome: { + fontSize: 20, + textAlign: "center", + margin: 10 + } +}); diff --git a/examples/video-caching/README.md b/examples/video-caching/README.md new file mode 100644 index 00000000..50269f44 --- /dev/null +++ b/examples/video-caching/README.md @@ -0,0 +1,24 @@ +# react-native-video caching example (currently only working on iOS) + +# How to verify that caching is working (iOS) + +1. run `./update.sh` +2. open `ios/VideoCaching.xcworkspace` +3. build and run project in simulator +4. after the video is loaded -> disconnect from the internet +5. kill the application +6. start the application again -> the video is there despite being offline :) + +# How to verify that you can build the project without the caching feature (iOS) + +1. In `ios/Podfile` apply the following changes +```diff +- pod 'react-native-video/VideoCaching', :path => '../node_modules/react-native-video/react-native-video.podspec' ++ pod 'react-native-video', :path => '../node_modules/react-native-video/react-native-video.podspec' +``` +2. run `./update.sh` +3. open `ios/VideoCaching.xcworkspace` +4. build and run project in simulator +5. after the video is loaded -> disconnect from the internet +6. kill the application +7. start the application again -> the video should not load diff --git a/examples/video-caching/__tests__/App.js b/examples/video-caching/__tests__/App.js new file mode 100644 index 00000000..d0b9ee31 --- /dev/null +++ b/examples/video-caching/__tests__/App.js @@ -0,0 +1,12 @@ +import 'react-native'; +import React from 'react'; +import App from '../App'; + +// Note: test renderer must be required after react-native. +import renderer from 'react-test-renderer'; + +it('renders correctly', () => { + const tree = renderer.create( + + ); +}); diff --git a/examples/video-caching/android/app/BUCK b/examples/video-caching/android/app/BUCK new file mode 100644 index 00000000..7b2b3ace --- /dev/null +++ b/examples/video-caching/android/app/BUCK @@ -0,0 +1,65 @@ +# To learn about Buck see [Docs](https://buckbuild.com/). +# To run your application with Buck: +# - install Buck +# - `npm start` - to start the packager +# - `cd android` +# - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` +# - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck +# - `buck install -r android/app` - compile, install and run application +# + +lib_deps = [] + +for jarfile in glob(['libs/*.jar']): + name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] + lib_deps.append(':' + name) + prebuilt_jar( + name = name, + binary_jar = jarfile, + ) + +for aarfile in glob(['libs/*.aar']): + name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] + lib_deps.append(':' + name) + android_prebuilt_aar( + name = name, + aar = aarfile, + ) + +android_library( + name = "all-libs", + exported_deps = lib_deps, +) + +android_library( + name = "app-code", + srcs = glob([ + "src/main/java/**/*.java", + ]), + deps = [ + ":all-libs", + ":build_config", + ":res", + ], +) + +android_build_config( + name = "build_config", + package = "com.videocaching", +) + +android_resource( + name = "res", + package = "com.videocaching", + res = "src/main/res", +) + +android_binary( + name = "app", + keystore = "//android/keystores:debug", + manifest = "src/main/AndroidManifest.xml", + package_type = "debug", + deps = [ + ":app-code", + ], +) diff --git a/examples/video-caching/android/app/build.gradle b/examples/video-caching/android/app/build.gradle new file mode 100644 index 00000000..7a6b5205 --- /dev/null +++ b/examples/video-caching/android/app/build.gradle @@ -0,0 +1,150 @@ +apply plugin: "com.android.application" + +import com.android.build.OutputFile + +/** + * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets + * and bundleReleaseJsAndAssets). + * These basically call `react-native bundle` with the correct arguments during the Android build + * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the + * bundle directly from the development server. Below you can see all the possible configurations + * and their defaults. If you decide to add a configuration block, make sure to add it before the + * `apply from: "../../node_modules/react-native/react.gradle"` line. + * + * project.ext.react = [ + * // the name of the generated asset file containing your JS bundle + * bundleAssetName: "index.android.bundle", + * + * // the entry file for bundle generation + * entryFile: "index.android.js", + * + * // whether to bundle JS and assets in debug mode + * bundleInDebug: false, + * + * // whether to bundle JS and assets in release mode + * bundleInRelease: true, + * + * // whether to bundle JS and assets in another build variant (if configured). + * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants + * // The configuration property can be in the following formats + * // 'bundleIn${productFlavor}${buildType}' + * // 'bundleIn${buildType}' + * // bundleInFreeDebug: true, + * // bundleInPaidRelease: true, + * // bundleInBeta: true, + * + * // whether to disable dev mode in custom build variants (by default only disabled in release) + * // for example: to disable dev mode in the staging build type (if configured) + * devDisabledInStaging: true, + * // The configuration property can be in the following formats + * // 'devDisabledIn${productFlavor}${buildType}' + * // 'devDisabledIn${buildType}' + * + * // the root of your project, i.e. where "package.json" lives + * root: "../../", + * + * // where to put the JS bundle asset in debug mode + * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", + * + * // where to put the JS bundle asset in release mode + * jsBundleDirRelease: "$buildDir/intermediates/assets/release", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in debug mode + * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", + * + * // where to put drawable resources / React Native assets, e.g. the ones you use via + * // require('./image.png')), in release mode + * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", + * + * // by default the gradle tasks are skipped if none of the JS files or assets change; this means + * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to + * // date; if you have any other folders that you want to ignore for performance reasons (gradle + * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ + * // for example, you might want to remove it from here. + * inputExcludes: ["android/**", "ios/**"], + * + * // override which node gets called and with what additional arguments + * nodeExecutableAndArgs: ["node"], + * + * // supply additional arguments to the packager + * extraPackagerArgs: [] + * ] + */ + +project.ext.react = [ + entryFile: "index.js" +] + +apply from: "../../node_modules/react-native/react.gradle" + +/** + * Set this to true to create two separate APKs instead of one: + * - An APK that only works on ARM devices + * - An APK that only works on x86 devices + * The advantage is the size of the APK is reduced by about 4MB. + * Upload all the APKs to the Play Store and people will download + * the correct one based on the CPU architecture of their device. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Run Proguard to shrink the Java bytecode in release builds. + */ +def enableProguardInReleaseBuilds = false + +android { + compileSdkVersion 23 + buildToolsVersion "23.0.1" + + defaultConfig { + applicationId "com.videocaching" + minSdkVersion 16 + targetSdkVersion 22 + versionCode 1 + versionName "1.0" + ndk { + abiFilters "armeabi-v7a", "x86" + } + } + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include "armeabi-v7a", "x86" + } + } + buildTypes { + release { + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits + def versionCodes = ["armeabi-v7a":1, "x86":2] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + versionCodes.get(abi) * 1048576 + defaultConfig.versionCode + } + } + } +} + +dependencies { + compile fileTree(dir: "libs", include: ["*.jar"]) + compile "com.android.support:appcompat-v7:23.0.1" + compile "com.facebook.react:react-native:+" // From node_modules +} + +// Run this once to be able to run the application with BUCK +// puts all compile dependencies into folder libs for BUCK to use +task copyDownloadableDepsToLibs(type: Copy) { + from configurations.compile + into 'libs' +} diff --git a/examples/video-caching/android/app/proguard-rules.pro b/examples/video-caching/android/app/proguard-rules.pro new file mode 100644 index 00000000..6e8516c8 --- /dev/null +++ b/examples/video-caching/android/app/proguard-rules.pro @@ -0,0 +1,70 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Disabling obfuscation is useful if you collect stack traces from production crashes +# (unless you are using a system that supports de-obfuscate the stack traces). +-dontobfuscate + +# React Native + +# Keep our interfaces so they can be used by other ProGuard rules. +# See http://sourceforge.net/p/proguard/bugs/466/ +-keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip +-keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters +-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip + +# Do not strip any method/class that is annotated with @DoNotStrip +-keep @com.facebook.proguard.annotations.DoNotStrip class * +-keep @com.facebook.common.internal.DoNotStrip class * +-keepclassmembers class * { + @com.facebook.proguard.annotations.DoNotStrip *; + @com.facebook.common.internal.DoNotStrip *; +} + +-keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { + void set*(***); + *** get*(); +} + +-keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } +-keep class * extends com.facebook.react.bridge.NativeModule { *; } +-keepclassmembers,includedescriptorclasses class * { native ; } +-keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } +-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } +-keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } + +-dontwarn com.facebook.react.** + +# TextLayoutBuilder uses a non-public Android constructor within StaticLayout. +# See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. +-dontwarn android.text.StaticLayout + +# okhttp + +-keepattributes Signature +-keepattributes *Annotation* +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** + +# okio + +-keep class sun.misc.Unsafe { *; } +-dontwarn java.nio.file.* +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement +-dontwarn okio.** diff --git a/examples/video-caching/android/app/src/main/AndroidManifest.xml b/examples/video-caching/android/app/src/main/AndroidManifest.xml new file mode 100644 index 00000000..c0050352 --- /dev/null +++ b/examples/video-caching/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/video-caching/android/app/src/main/java/com/videocaching/MainActivity.java b/examples/video-caching/android/app/src/main/java/com/videocaching/MainActivity.java new file mode 100644 index 00000000..65764401 --- /dev/null +++ b/examples/video-caching/android/app/src/main/java/com/videocaching/MainActivity.java @@ -0,0 +1,15 @@ +package com.videocaching; + +import com.facebook.react.ReactActivity; + +public class MainActivity extends ReactActivity { + + /** + * Returns the name of the main component registered from JavaScript. + * This is used to schedule rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "VideoCaching"; + } +} diff --git a/examples/video-caching/android/app/src/main/java/com/videocaching/MainApplication.java b/examples/video-caching/android/app/src/main/java/com/videocaching/MainApplication.java new file mode 100644 index 00000000..5d02478e --- /dev/null +++ b/examples/video-caching/android/app/src/main/java/com/videocaching/MainApplication.java @@ -0,0 +1,45 @@ +package com.videocaching; + +import android.app.Application; + +import com.facebook.react.ReactApplication; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.shell.MainReactPackage; +import com.facebook.soloader.SoLoader; + +import java.util.Arrays; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + return Arrays.asList( + new MainReactPackage() + ); + } + + @Override + protected String getJSMainModuleName() { + return "index"; + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + } +} diff --git a/examples/video-caching/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/examples/video-caching/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 00000000..cde69bcc Binary files /dev/null and b/examples/video-caching/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/examples/video-caching/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/examples/video-caching/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 00000000..c133a0cb Binary files /dev/null and b/examples/video-caching/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/examples/video-caching/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/examples/video-caching/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 00000000..bfa42f0e Binary files /dev/null and b/examples/video-caching/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/examples/video-caching/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/examples/video-caching/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 00000000..324e72cd Binary files /dev/null and b/examples/video-caching/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/examples/video-caching/android/app/src/main/res/values/strings.xml b/examples/video-caching/android/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..ff7ee6da --- /dev/null +++ b/examples/video-caching/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + VideoCaching + diff --git a/examples/video-caching/android/app/src/main/res/values/styles.xml b/examples/video-caching/android/app/src/main/res/values/styles.xml new file mode 100644 index 00000000..319eb0ca --- /dev/null +++ b/examples/video-caching/android/app/src/main/res/values/styles.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/examples/video-caching/android/build.gradle b/examples/video-caching/android/build.gradle new file mode 100644 index 00000000..eed9972b --- /dev/null +++ b/examples/video-caching/android/build.gradle @@ -0,0 +1,24 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + jcenter() + } + dependencies { + classpath 'com.android.tools.build:gradle:2.2.3' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +allprojects { + repositories { + mavenLocal() + jcenter() + maven { + // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm + url "$rootDir/../node_modules/react-native/android" + } + } +} diff --git a/examples/video-caching/android/gradle.properties b/examples/video-caching/android/gradle.properties new file mode 100644 index 00000000..1fd964e9 --- /dev/null +++ b/examples/video-caching/android/gradle.properties @@ -0,0 +1,20 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +android.useDeprecatedNdk=true diff --git a/examples/video-caching/android/gradle/wrapper/gradle-wrapper.jar b/examples/video-caching/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..b5166dad Binary files /dev/null and b/examples/video-caching/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/video-caching/android/gradle/wrapper/gradle-wrapper.properties b/examples/video-caching/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..dbdc05d2 --- /dev/null +++ b/examples/video-caching/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/examples/video-caching/android/gradlew b/examples/video-caching/android/gradlew new file mode 100755 index 00000000..91a7e269 --- /dev/null +++ b/examples/video-caching/android/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/examples/video-caching/android/gradlew.bat b/examples/video-caching/android/gradlew.bat new file mode 100644 index 00000000..aec99730 --- /dev/null +++ b/examples/video-caching/android/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/video-caching/android/keystores/BUCK b/examples/video-caching/android/keystores/BUCK new file mode 100644 index 00000000..88e4c31b --- /dev/null +++ b/examples/video-caching/android/keystores/BUCK @@ -0,0 +1,8 @@ +keystore( + name = "debug", + properties = "debug.keystore.properties", + store = "debug.keystore", + visibility = [ + "PUBLIC", + ], +) diff --git a/examples/video-caching/android/keystores/debug.keystore.properties b/examples/video-caching/android/keystores/debug.keystore.properties new file mode 100644 index 00000000..121bfb49 --- /dev/null +++ b/examples/video-caching/android/keystores/debug.keystore.properties @@ -0,0 +1,4 @@ +key.store=debug.keystore +key.alias=androiddebugkey +key.store.password=android +key.alias.password=android diff --git a/examples/video-caching/android/settings.gradle b/examples/video-caching/android/settings.gradle new file mode 100644 index 00000000..b95fe3ac --- /dev/null +++ b/examples/video-caching/android/settings.gradle @@ -0,0 +1,3 @@ +rootProject.name = 'VideoCaching' + +include ':app' diff --git a/examples/video-caching/app.json b/examples/video-caching/app.json new file mode 100644 index 00000000..987af226 --- /dev/null +++ b/examples/video-caching/app.json @@ -0,0 +1,4 @@ +{ + "name": "VideoCaching", + "displayName": "VideoCaching" +} \ No newline at end of file diff --git a/examples/video-caching/index.js b/examples/video-caching/index.js new file mode 100644 index 00000000..afca0eb0 --- /dev/null +++ b/examples/video-caching/index.js @@ -0,0 +1,4 @@ +import { AppRegistry, Platform } from "react-native"; +import App from "./App"; + +AppRegistry.registerComponent("VideoCaching", () => App); diff --git a/examples/video-caching/ios/Podfile b/examples/video-caching/ios/Podfile new file mode 100644 index 00000000..4decf7e1 --- /dev/null +++ b/examples/video-caching/ios/Podfile @@ -0,0 +1,32 @@ +# Uncomment the next line to define a global platform for your project +platform :ios, '10.0' + +target 'VideoCaching' do + rn_path = '../node_modules/react-native' + + pod 'yoga', path: "#{rn_path}/ReactCommon/yoga/yoga.podspec" + pod 'DoubleConversion', :podspec => "#{rn_path}/third-party-podspecs/DoubleConversion.podspec" + pod 'Folly', :podspec => "#{rn_path}/third-party-podspecs/Folly.podspec" + pod 'glog', :podspec => "#{rn_path}/third-party-podspecs/GLog.podspec" + pod 'React', path: rn_path, subspecs: [ + 'Core', + 'CxxBridge', + 'RCTAnimation', + 'RCTActionSheet', + 'RCTImage', + 'RCTLinkingIOS', + 'RCTNetwork', + 'RCTSettings', + 'RCTText', + 'RCTVibration', + 'RCTWebSocket', + 'RCTPushNotification', + 'RCTCameraRoll', + 'RCTSettings', + 'RCTBlob', + 'RCTGeolocation', + 'DevSupport' + ] + + pod 'react-native-video/VideoCaching', :path => '../node_modules/react-native-video/react-native-video.podspec' +end diff --git a/examples/video-caching/ios/Podfile.lock b/examples/video-caching/ios/Podfile.lock new file mode 100644 index 00000000..2689020a --- /dev/null +++ b/examples/video-caching/ios/Podfile.lock @@ -0,0 +1,128 @@ +PODS: + - boost-for-react-native (1.63.0) + - DoubleConversion (1.1.5) + - DVAssetLoaderDelegate (0.3.2) + - Folly (2016.10.31.00): + - boost-for-react-native + - DoubleConversion + - glog + - glog (0.3.4) + - React (0.56.0): + - React/Core (= 0.56.0) + - react-native-video/Video (3.1.0): + - React + - react-native-video/VideoCaching (3.1.0): + - DVAssetLoaderDelegate (~> 0.3.1) + - React + - react-native-video/Video + - SPTPersistentCache (~> 1.1.0) + - React/Core (0.56.0): + - yoga (= 0.56.0.React) + - React/CxxBridge (0.56.0): + - Folly (= 2016.10.31.00) + - React/Core + - React/cxxreact + - React/cxxreact (0.56.0): + - boost-for-react-native (= 1.63.0) + - Folly (= 2016.10.31.00) + - React/jschelpers + - React/jsinspector + - React/DevSupport (0.56.0): + - React/Core + - React/RCTWebSocket + - React/fishhook (0.56.0) + - React/jschelpers (0.56.0): + - Folly (= 2016.10.31.00) + - React/PrivateDatabase + - React/jsinspector (0.56.0) + - React/PrivateDatabase (0.56.0) + - React/RCTActionSheet (0.56.0): + - React/Core + - React/RCTAnimation (0.56.0): + - React/Core + - React/RCTBlob (0.56.0): + - React/Core + - React/RCTCameraRoll (0.56.0): + - React/Core + - React/RCTImage + - React/RCTGeolocation (0.56.0): + - React/Core + - React/RCTImage (0.56.0): + - React/Core + - React/RCTNetwork + - React/RCTLinkingIOS (0.56.0): + - React/Core + - React/RCTNetwork (0.56.0): + - React/Core + - React/RCTPushNotification (0.56.0): + - React/Core + - React/RCTSettings (0.56.0): + - React/Core + - React/RCTText (0.56.0): + - React/Core + - React/RCTVibration (0.56.0): + - React/Core + - React/RCTWebSocket (0.56.0): + - React/Core + - React/fishhook + - React/RCTBlob + - SPTPersistentCache (1.1.0) + - yoga (0.56.0.React) + +DEPENDENCIES: + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`) + - glog (from `../node_modules/react-native/third-party-podspecs/GLog.podspec`) + - react-native-video/VideoCaching (from `../node_modules/react-native-video/react-native-video.podspec`) + - React/Core (from `../node_modules/react-native`) + - React/CxxBridge (from `../node_modules/react-native`) + - React/DevSupport (from `../node_modules/react-native`) + - React/RCTActionSheet (from `../node_modules/react-native`) + - React/RCTAnimation (from `../node_modules/react-native`) + - React/RCTBlob (from `../node_modules/react-native`) + - React/RCTCameraRoll (from `../node_modules/react-native`) + - React/RCTGeolocation (from `../node_modules/react-native`) + - React/RCTImage (from `../node_modules/react-native`) + - React/RCTLinkingIOS (from `../node_modules/react-native`) + - React/RCTNetwork (from `../node_modules/react-native`) + - React/RCTPushNotification (from `../node_modules/react-native`) + - React/RCTSettings (from `../node_modules/react-native`) + - React/RCTText (from `../node_modules/react-native`) + - React/RCTVibration (from `../node_modules/react-native`) + - React/RCTWebSocket (from `../node_modules/react-native`) + - yoga (from `../node_modules/react-native/ReactCommon/yoga/yoga.podspec`) + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - boost-for-react-native + - DVAssetLoaderDelegate + - SPTPersistentCache + +EXTERNAL SOURCES: + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/Folly.podspec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/GLog.podspec" + React: + :path: "../node_modules/react-native" + react-native-video: + :path: "../node_modules/react-native-video/react-native-video.podspec" + yoga: + :path: "../node_modules/react-native/ReactCommon/yoga/yoga.podspec" + +SPEC CHECKSUMS: + boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c + DoubleConversion: a9706f16e388b53ff12cca34473428ee29746a26 + DVAssetLoaderDelegate: 38a24530292bf77900fdfdf635434f7f9b49486d + Folly: c89ac2d5c6ab169cd7397ef27485c44f35f742c7 + glog: b3b0330915eccea41c5cc9731a77cf564a9be5ea + React: 1fe0eb13d90b625d94c3b117c274dcfd2e760e11 + react-native-video: 44c6befbc1526283ca1919891fcebe4680feade4 + SPTPersistentCache: df36ea46762d7cf026502bbb86a8b79d0080dff4 + yoga: b1ce48b6cf950b98deae82838f5173ea7cf89e85 + +PODFILE CHECKSUM: f4123c35c77493d6ddbcb86898737abdf5e0fac8 + +COCOAPODS: 1.5.3 diff --git a/examples/video-caching/ios/VideoCaching-tvOS/Info.plist b/examples/video-caching/ios/VideoCaching-tvOS/Info.plist new file mode 100644 index 00000000..2fb6a11c --- /dev/null +++ b/examples/video-caching/ios/VideoCaching-tvOS/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + NSAppTransportSecurity + + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + + diff --git a/examples/video-caching/ios/VideoCaching-tvOSTests/Info.plist b/examples/video-caching/ios/VideoCaching-tvOSTests/Info.plist new file mode 100644 index 00000000..886825cc --- /dev/null +++ b/examples/video-caching/ios/VideoCaching-tvOSTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/examples/video-caching/ios/VideoCaching.xcodeproj/project.pbxproj b/examples/video-caching/ios/VideoCaching.xcodeproj/project.pbxproj new file mode 100644 index 00000000..a64f4efd --- /dev/null +++ b/examples/video-caching/ios/VideoCaching.xcodeproj/project.pbxproj @@ -0,0 +1,1495 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; }; + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; }; + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */; }; + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; }; + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; }; + 00E356F31AD99517003FC87E /* VideoCachingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* VideoCachingTests.m */; }; + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; }; + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; }; + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 146834051AC3E58100842450 /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 146834041AC3E56700842450 /* libReact.a */; }; + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; }; + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; + 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */; }; + 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */; }; + 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */; }; + 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */; }; + 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */; }; + 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */; }; + 2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D16E6891FA4F8E400B85C8A /* libReact.a */; }; + 2DCD954D1E0B4F2C00145EB5 /* VideoCachingTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* VideoCachingTests.m */; }; + 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; }; + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; }; + ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */; }; + D1FC585BCEC69367C235A632 /* libPods-VideoCaching.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 08B9A2A39DE457E6FCFA0DF3 /* libPods-VideoCaching.a */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTActionSheet; + }; + 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTGeolocation; + }; + 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5115D1A9E6B3D00147676; + remoteInfo = RCTImage; + }; + 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B511DB1A9E6C8500147676; + remoteInfo = RCTNetwork; + }; + 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 832C81801AAF6DEF007FA2F7; + remoteInfo = RCTVibration; + }; + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = VideoCaching; + }; + 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTSettings; + }; + 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3C86DF461ADF2C930047B81A; + remoteInfo = RCTWebSocket; + }; + 146834031AC3E56700842450 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 83CBBA2E1A601D0E00E9B192; + remoteInfo = React; + }; + 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 2D02E47A1E0B4A5D006451C7; + remoteInfo = "VideoCaching-tvOS"; + }; + 2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = ADD01A681E09402E00F6D226; + remoteInfo = "RCTBlob-tvOS"; + }; + 2D16E6831FA4F8DC00B85C8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D001F3B181A0099AA32; + remoteInfo = fishhook; + }; + 2D16E6851FA4F8DC00B85C8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3DBE0D0D1F3B181C0099AA32; + remoteInfo = "fishhook-tvOS"; + }; + 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A283A1D9B042B00D4039D; + remoteInfo = "RCTImage-tvOS"; + }; + 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28471D9B043800D4039D; + remoteInfo = "RCTLinking-tvOS"; + }; + 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28541D9B044C00D4039D; + remoteInfo = "RCTNetwork-tvOS"; + }; + 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28611D9B046600D4039D; + remoteInfo = "RCTSettings-tvOS"; + }; + 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A287B1D9B048500D4039D; + remoteInfo = "RCTText-tvOS"; + }; + 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28881D9B049200D4039D; + remoteInfo = "RCTWebSocket-tvOS"; + }; + 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28131D9B038B00D4039D; + remoteInfo = "React-tvOS"; + }; + 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3C059A1DE3340900C268FA; + remoteInfo = yoga; + }; + 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3C06751DE3340C00C268FA; + remoteInfo = "yoga-tvOS"; + }; + 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD9251DE5FBEC00167DC4; + remoteInfo = cxxreact; + }; + 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD9321DE5FBEE00167DC4; + remoteInfo = "cxxreact-tvOS"; + }; + 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD90B1DE5FBD600167DC4; + remoteInfo = jschelpers; + }; + 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D3CD9181DE5FBD800167DC4; + remoteInfo = "jschelpers-tvOS"; + }; + 499AA47B2048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BDC1FC498900052F4D5; + remoteInfo = jsinspector; + }; + 499AA47D2048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = EBF21BFA1FC4989A0052F4D5; + remoteInfo = "jsinspector-tvOS"; + }; + 499AA47F2048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7ECE1E25DB7D00323FB7; + remoteInfo = "third-party"; + }; + 499AA4812048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D3C1EBD27B6005632C8; + remoteInfo = "third-party-tvOS"; + }; + 499AA4832048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 139D7E881E25C6D100323FB7; + remoteInfo = "double-conversion"; + }; + 499AA4852048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 3D383D621EBD27B9005632C8; + remoteInfo = "double-conversion-tvOS"; + }; + 499AA4872048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F3131F5F2E4B0010BF04; + remoteInfo = privatedata; + }; + 499AA4892048CC200004ACC3 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 146833FF1AC3E56700842450 /* React.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04; + remoteInfo = "privatedata-tvOS"; + }; + 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTAnimation; + }; + 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 2D2A28201D9B03D100D4039D; + remoteInfo = "RCTAnimation-tvOS"; + }; + 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 134814201AA4EA6300B7C361; + remoteInfo = RCTLinking; + }; + 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 58B5119B1A9E6C1200147676; + remoteInfo = RCTText; + }; + ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 358F4ED71D1E81A9004DF814; + remoteInfo = RCTBlob; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 008F07F21AC5B25A0029DE68 /* main.jsbundle */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = main.jsbundle; sourceTree = ""; }; + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTActionSheet.xcodeproj; path = "../node_modules/react-native/Libraries/ActionSheetIOS/RCTActionSheet.xcodeproj"; sourceTree = ""; }; + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTGeolocation.xcodeproj; path = "../node_modules/react-native/Libraries/Geolocation/RCTGeolocation.xcodeproj"; sourceTree = ""; }; + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTImage.xcodeproj; path = "../node_modules/react-native/Libraries/Image/RCTImage.xcodeproj"; sourceTree = ""; }; + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTNetwork.xcodeproj; path = "../node_modules/react-native/Libraries/Network/RCTNetwork.xcodeproj"; sourceTree = ""; }; + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTVibration.xcodeproj; path = "../node_modules/react-native/Libraries/Vibration/RCTVibration.xcodeproj"; sourceTree = ""; }; + 00E356EE1AD99517003FC87E /* VideoCachingTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VideoCachingTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* VideoCachingTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VideoCachingTests.m; sourceTree = ""; }; + 08B9A2A39DE457E6FCFA0DF3 /* libPods-VideoCaching.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-VideoCaching.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 09C735C3EBC6DB072F4866AE /* Pods-VideoCaching.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoCaching.debug.xcconfig"; path = "Pods/Target Support Files/Pods-VideoCaching/Pods-VideoCaching.debug.xcconfig"; sourceTree = ""; }; + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTSettings.xcodeproj; path = "../node_modules/react-native/Libraries/Settings/RCTSettings.xcodeproj"; sourceTree = ""; }; + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTWebSocket.xcodeproj; path = "../node_modules/react-native/Libraries/WebSocket/RCTWebSocket.xcodeproj"; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* VideoCaching.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = VideoCaching.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = VideoCaching/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = AppDelegate.m; path = VideoCaching/AppDelegate.m; sourceTree = ""; }; + 13B07FB21A68108700A75B9A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = VideoCaching/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = VideoCaching/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = VideoCaching/main.m; sourceTree = ""; }; + 146833FF1AC3E56700842450 /* React.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = React.xcodeproj; path = "../node_modules/react-native/React/React.xcodeproj"; sourceTree = ""; }; + 2D02E47B1E0B4A5D006451C7 /* VideoCaching-tvOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "VideoCaching-tvOS.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D02E4901E0B4A5D006451C7 /* VideoCaching-tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "VideoCaching-tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 2D16E6891FA4F8E400B85C8A /* libReact.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; path = libReact.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = ""; }; + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTLinking.xcodeproj; path = "../node_modules/react-native/Libraries/LinkingIOS/RCTLinking.xcodeproj"; sourceTree = ""; }; + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTText.xcodeproj; path = "../node_modules/react-native/Libraries/Text/RCTText.xcodeproj"; sourceTree = ""; }; + ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = ""; }; + F26E57FA9826531B8B01D2A9 /* Pods-VideoCaching.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-VideoCaching.release.xcconfig"; path = "Pods/Target Support Files/Pods-VideoCaching/Pods-VideoCaching.release.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 140ED2AC1D01E1AD002B40FF /* libReact.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ADBDB9381DFEBF1600ED6528 /* libRCTBlob.a in Frameworks */, + 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, + 146834051AC3E58100842450 /* libReact.a in Frameworks */, + 5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */, + 00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */, + 00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */, + 00C302E81ABCBA2D00DB3ED1 /* libRCTImage.a in Frameworks */, + 133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */, + 00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */, + 139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */, + 832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */, + 00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */, + 139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */, + D1FC585BCEC69367C235A632 /* libPods-VideoCaching.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4781E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D16E6881FA4F8E400B85C8A /* libReact.a in Frameworks */, + 2D02E4C21E0B4AEC006451C7 /* libRCTAnimation.a in Frameworks */, + 2D02E4C31E0B4AEC006451C7 /* libRCTImage-tvOS.a in Frameworks */, + 2D02E4C41E0B4AEC006451C7 /* libRCTLinking-tvOS.a in Frameworks */, + 2D02E4C51E0B4AEC006451C7 /* libRCTNetwork-tvOS.a in Frameworks */, + 2D02E4C61E0B4AEC006451C7 /* libRCTSettings-tvOS.a in Frameworks */, + 2D02E4C71E0B4AEC006451C7 /* libRCTText-tvOS.a in Frameworks */, + 2D02E4C81E0B4AEC006451C7 /* libRCTWebSocket-tvOS.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48D1E0B4A5D006451C7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00C302A81ABCB8CE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302B61ABCB90400DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302BC1ABCB91800DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */, + 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302D41ABCB9D200DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */, + 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 00C302E01ABCB9EE00DB3ED1 /* Products */ = { + isa = PBXGroup; + children = ( + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */, + ); + name = Products; + sourceTree = ""; + }; + 00E356EF1AD99517003FC87E /* VideoCachingTests */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* VideoCachingTests.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = VideoCachingTests; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 139105B71AF99BAD00B5F7CC /* Products */ = { + isa = PBXGroup; + children = ( + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */, + 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 139FDEE71B06529A00C62182 /* Products */ = { + isa = PBXGroup; + children = ( + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */, + 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */, + 2D16E6841FA4F8DC00B85C8A /* libfishhook.a */, + 2D16E6861FA4F8DC00B85C8A /* libfishhook-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* VideoCaching */ = { + isa = PBXGroup; + children = ( + 008F07F21AC5B25A0029DE68 /* main.jsbundle */, + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.m */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = VideoCaching; + sourceTree = ""; + }; + 146834001AC3E56700842450 /* Products */ = { + isa = PBXGroup; + children = ( + 146834041AC3E56700842450 /* libReact.a */, + 3DAD3EA31DF850E9000B6D8A /* libReact.a */, + 3DAD3EA51DF850E9000B6D8A /* libyoga.a */, + 3DAD3EA71DF850E9000B6D8A /* libyoga.a */, + 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */, + 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */, + 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */, + 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */, + 499AA47C2048CC200004ACC3 /* libjsinspector.a */, + 499AA47E2048CC200004ACC3 /* libjsinspector-tvOS.a */, + 499AA4802048CC200004ACC3 /* libthird-party.a */, + 499AA4822048CC200004ACC3 /* libthird-party.a */, + 499AA4842048CC200004ACC3 /* libdouble-conversion.a */, + 499AA4862048CC200004ACC3 /* libdouble-conversion.a */, + 499AA4882048CC200004ACC3 /* libprivatedata.a */, + 499AA48A2048CC200004ACC3 /* libprivatedata-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + 2D16E6891FA4F8E400B85C8A /* libReact.a */, + 08B9A2A39DE457E6FCFA0DF3 /* libPods-VideoCaching.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 5E91572E1DD0AC6500FF2AA8 /* Products */ = { + isa = PBXGroup; + children = ( + 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */, + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */, + ); + name = Products; + sourceTree = ""; + }; + 78C398B11ACF4ADC00677621 /* Products */ = { + isa = PBXGroup; + children = ( + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */, + 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */, + 146833FF1AC3E56700842450 /* React.xcodeproj */, + 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */, + ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */, + 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */, + 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */, + 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */, + 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */, + 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */, + 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */, + 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */, + 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */, + ); + name = Libraries; + sourceTree = ""; + }; + 832341B11AAA6A8300B99B32 /* Products */ = { + isa = PBXGroup; + children = ( + 832341B51AAA6A8300B99B32 /* libRCTText.a */, + 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* VideoCaching */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* VideoCachingTests */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + C56355F3A1157B4497284CC6 /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* VideoCaching.app */, + 00E356EE1AD99517003FC87E /* VideoCachingTests.xctest */, + 2D02E47B1E0B4A5D006451C7 /* VideoCaching-tvOS.app */, + 2D02E4901E0B4A5D006451C7 /* VideoCaching-tvOSTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + ADBDB9201DFEBF0600ED6528 /* Products */ = { + isa = PBXGroup; + children = ( + ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */, + 2D16E6721FA4F8DC00B85C8A /* libRCTBlob-tvOS.a */, + ); + name = Products; + sourceTree = ""; + }; + C56355F3A1157B4497284CC6 /* Pods */ = { + isa = PBXGroup; + children = ( + 09C735C3EBC6DB072F4866AE /* Pods-VideoCaching.debug.xcconfig */, + F26E57FA9826531B8B01D2A9 /* Pods-VideoCaching.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* VideoCachingTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "VideoCachingTests" */; + buildPhases = ( + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = VideoCachingTests; + productName = VideoCachingTests; + productReference = 00E356EE1AD99517003FC87E /* VideoCachingTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* VideoCaching */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VideoCaching" */; + buildPhases = ( + 115319CC17BBCF9B0C5519F6 /* [CP] Check Pods Manifest.lock */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = VideoCaching; + productName = "Hello World"; + productReference = 13B07F961A680F5B00A75B9A /* VideoCaching.app */; + productType = "com.apple.product-type.application"; + }; + 2D02E47A1E0B4A5D006451C7 /* VideoCaching-tvOS */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoCaching-tvOS" */; + buildPhases = ( + 2D02E4771E0B4A5D006451C7 /* Sources */, + 2D02E4781E0B4A5D006451C7 /* Frameworks */, + 2D02E4791E0B4A5D006451C7 /* Resources */, + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "VideoCaching-tvOS"; + productName = "VideoCaching-tvOS"; + productReference = 2D02E47B1E0B4A5D006451C7 /* VideoCaching-tvOS.app */; + productType = "com.apple.product-type.application"; + }; + 2D02E48F1E0B4A5D006451C7 /* VideoCaching-tvOSTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoCaching-tvOSTests" */; + buildPhases = ( + 2D02E48C1E0B4A5D006451C7 /* Sources */, + 2D02E48D1E0B4A5D006451C7 /* Frameworks */, + 2D02E48E1E0B4A5D006451C7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */, + ); + name = "VideoCaching-tvOSTests"; + productName = "VideoCaching-tvOSTests"; + productReference = 2D02E4901E0B4A5D006451C7 /* VideoCaching-tvOSTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = Facebook; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 2D02E47A1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + }; + 2D02E48F1E0B4A5D006451C7 = { + CreatedOnToolsVersion = 8.2.1; + ProvisioningStyle = Automatic; + TestTargetID = 2D02E47A1E0B4A5D006451C7; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoCaching" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 00C302A81ABCB8CE00DB3ED1 /* Products */; + ProjectRef = 00C302A71ABCB8CE00DB3ED1 /* RCTActionSheet.xcodeproj */; + }, + { + ProductGroup = 5E91572E1DD0AC6500FF2AA8 /* Products */; + ProjectRef = 5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */; + }, + { + ProductGroup = ADBDB9201DFEBF0600ED6528 /* Products */; + ProjectRef = ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */; + }, + { + ProductGroup = 00C302B61ABCB90400DB3ED1 /* Products */; + ProjectRef = 00C302B51ABCB90400DB3ED1 /* RCTGeolocation.xcodeproj */; + }, + { + ProductGroup = 00C302BC1ABCB91800DB3ED1 /* Products */; + ProjectRef = 00C302BB1ABCB91800DB3ED1 /* RCTImage.xcodeproj */; + }, + { + ProductGroup = 78C398B11ACF4ADC00677621 /* Products */; + ProjectRef = 78C398B01ACF4ADC00677621 /* RCTLinking.xcodeproj */; + }, + { + ProductGroup = 00C302D41ABCB9D200DB3ED1 /* Products */; + ProjectRef = 00C302D31ABCB9D200DB3ED1 /* RCTNetwork.xcodeproj */; + }, + { + ProductGroup = 139105B71AF99BAD00B5F7CC /* Products */; + ProjectRef = 139105B61AF99BAD00B5F7CC /* RCTSettings.xcodeproj */; + }, + { + ProductGroup = 832341B11AAA6A8300B99B32 /* Products */; + ProjectRef = 832341B01AAA6A8300B99B32 /* RCTText.xcodeproj */; + }, + { + ProductGroup = 00C302E01ABCB9EE00DB3ED1 /* Products */; + ProjectRef = 00C302DF1ABCB9EE00DB3ED1 /* RCTVibration.xcodeproj */; + }, + { + ProductGroup = 139FDEE71B06529A00C62182 /* Products */; + ProjectRef = 139FDEE61B06529A00C62182 /* RCTWebSocket.xcodeproj */; + }, + { + ProductGroup = 146834001AC3E56700842450 /* Products */; + ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* VideoCaching */, + 00E356ED1AD99517003FC87E /* VideoCachingTests */, + 2D02E47A1E0B4A5D006451C7 /* VideoCaching-tvOS */, + 2D02E48F1E0B4A5D006451C7 /* VideoCaching-tvOSTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTActionSheet.a; + remoteRef = 00C302AB1ABCB8CE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTGeolocation.a; + remoteRef = 00C302B91ABCB90400DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302C01ABCB91800DB3ED1 /* libRCTImage.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTImage.a; + remoteRef = 00C302BF1ABCB91800DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTNetwork.a; + remoteRef = 00C302DB1ABCB9D200DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTVibration.a; + remoteRef = 00C302E31ABCB9EE00DB3ED1 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTSettings.a; + remoteRef = 139105C01AF99BAD00B5F7CC /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTWebSocket.a; + remoteRef = 139FDEF31B06529B00C62182 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 146834041AC3E56700842450 /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 146834031AC3E56700842450 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2D16E6721FA4F8DC00B85C8A /* libRCTBlob-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTBlob-tvOS.a"; + remoteRef = 2D16E6711FA4F8DC00B85C8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2D16E6841FA4F8DC00B85C8A /* libfishhook.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libfishhook.a; + remoteRef = 2D16E6831FA4F8DC00B85C8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 2D16E6861FA4F8DC00B85C8A /* libfishhook-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libfishhook-tvOS.a"; + remoteRef = 2D16E6851FA4F8DC00B85C8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E841DF850E9000B6D8A /* libRCTImage-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTImage-tvOS.a"; + remoteRef = 3DAD3E831DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E881DF850E9000B6D8A /* libRCTLinking-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTLinking-tvOS.a"; + remoteRef = 3DAD3E871DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E8C1DF850E9000B6D8A /* libRCTNetwork-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTNetwork-tvOS.a"; + remoteRef = 3DAD3E8B1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E901DF850E9000B6D8A /* libRCTSettings-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTSettings-tvOS.a"; + remoteRef = 3DAD3E8F1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E941DF850E9000B6D8A /* libRCTText-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTText-tvOS.a"; + remoteRef = 3DAD3E931DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3E991DF850E9000B6D8A /* libRCTWebSocket-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libRCTWebSocket-tvOS.a"; + remoteRef = 3DAD3E981DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA31DF850E9000B6D8A /* libReact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libReact.a; + remoteRef = 3DAD3EA21DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA51DF850E9000B6D8A /* libyoga.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libyoga.a; + remoteRef = 3DAD3EA41DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA71DF850E9000B6D8A /* libyoga.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libyoga.a; + remoteRef = 3DAD3EA61DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EA91DF850E9000B6D8A /* libcxxreact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libcxxreact.a; + remoteRef = 3DAD3EA81DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EAB1DF850E9000B6D8A /* libcxxreact.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libcxxreact.a; + remoteRef = 3DAD3EAA1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EAD1DF850E9000B6D8A /* libjschelpers.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjschelpers.a; + remoteRef = 3DAD3EAC1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 3DAD3EAF1DF850E9000B6D8A /* libjschelpers.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjschelpers.a; + remoteRef = 3DAD3EAE1DF850E9000B6D8A /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA47C2048CC200004ACC3 /* libjsinspector.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libjsinspector.a; + remoteRef = 499AA47B2048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA47E2048CC200004ACC3 /* libjsinspector-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libjsinspector-tvOS.a"; + remoteRef = 499AA47D2048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA4802048CC200004ACC3 /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 499AA47F2048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA4822048CC200004ACC3 /* libthird-party.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libthird-party.a"; + remoteRef = 499AA4812048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA4842048CC200004ACC3 /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = 499AA4832048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA4862048CC200004ACC3 /* libdouble-conversion.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libdouble-conversion.a"; + remoteRef = 499AA4852048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA4882048CC200004ACC3 /* libprivatedata.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libprivatedata.a; + remoteRef = 499AA4872048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 499AA48A2048CC200004ACC3 /* libprivatedata-tvOS.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = "libprivatedata-tvOS.a"; + remoteRef = 499AA4892048CC200004ACC3 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 5E9157321DD0AC6500FF2AA8 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 5E9157351DD0AC6500FF2AA8 /* libRCTAnimation.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTAnimation.a; + remoteRef = 5E9157341DD0AC6500FF2AA8 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 78C398B91ACF4ADC00677621 /* libRCTLinking.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTLinking.a; + remoteRef = 78C398B81ACF4ADC00677621 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 832341B51AAA6A8300B99B32 /* libRCTText.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTText.a; + remoteRef = 832341B41AAA6A8300B99B32 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + ADBDB9271DFEBF0700ED6528 /* libRCTBlob.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libRCTBlob.a; + remoteRef = ADBDB9261DFEBF0700ED6528 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + 13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4791E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BD1E0B4A84006451C7 /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48E1E0B4A5D006451C7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + }; + 115319CC17BBCF9B0C5519F6 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-VideoCaching-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + 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"; + showEnvVarsInLog = 0; + }; + 2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Bundle React Native Code And Images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* VideoCachingTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E4771E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2D02E4BF1E0B4AB3006451C7 /* main.m in Sources */, + 2D02E4BC1E0B4A80006451C7 /* AppDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 2D02E48C1E0B4A5D006451C7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2DCD954D1E0B4F2C00145EB5 /* VideoCachingTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* VideoCaching */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; + 2D02E4921E0B4A5D006451C7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 2D02E47A1E0B4A5D006451C7 /* VideoCaching-tvOS */; + targetProxy = 2D02E4911E0B4A5D006451C7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = { + isa = PBXVariantGroup; + children = ( + 13B07FB21A68108700A75B9A /* Base */, + ); + name = LaunchScreen.xib; + path = VideoCaching; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = VideoCachingTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoCaching.app/VideoCaching"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = VideoCachingTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoCaching.app/VideoCaching"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 09C735C3EBC6DB072F4866AE /* Pods-VideoCaching.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = NO; + INFOPLIST_FILE = VideoCaching/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_NAME = VideoCaching; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F26E57FA9826531B8B01D2A9 /* Pods-VideoCaching.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = 1; + INFOPLIST_FILE = VideoCaching/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_NAME = VideoCaching; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 2D02E4971E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoCaching-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.VideoCaching-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.2; + }; + name = Debug; + }; + 2D02E4981E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoCaching-tvOS/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.VideoCaching-tvOS"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 9.2; + }; + name = Release; + }; + 2D02E4991E0B4A5E006451C7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_TESTABILITY = YES; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoCaching-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.VideoCaching-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoCaching-tvOS.app/VideoCaching-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Debug; + }; + 2D02E49A1E0B4A5E006451C7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_NO_COMMON_BLOCKS = YES; + INFOPLIST_FILE = "VideoCaching-tvOSTests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = "com.facebook.REACT.VideoCaching-tvOSTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/VideoCaching-tvOS.app/VideoCaching-tvOS"; + TVOS_DEPLOYMENT_TARGET = 10.1; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "VideoCachingTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "VideoCaching" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D02E4BA1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoCaching-tvOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4971E0B4A5E006451C7 /* Debug */, + 2D02E4981E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D02E4BB1E0B4A5E006451C7 /* Build configuration list for PBXNativeTarget "VideoCaching-tvOSTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 2D02E4991E0B4A5E006451C7 /* Debug */, + 2D02E49A1E0B4A5E006451C7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "VideoCaching" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/examples/video-caching/ios/VideoCaching.xcodeproj/xcshareddata/xcschemes/VideoCaching-tvOS.xcscheme b/examples/video-caching/ios/VideoCaching.xcodeproj/xcshareddata/xcschemes/VideoCaching-tvOS.xcscheme new file mode 100644 index 00000000..82aea837 --- /dev/null +++ b/examples/video-caching/ios/VideoCaching.xcodeproj/xcshareddata/xcschemes/VideoCaching-tvOS.xcscheme @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/video-caching/ios/VideoCaching.xcodeproj/xcshareddata/xcschemes/VideoCaching.xcscheme b/examples/video-caching/ios/VideoCaching.xcodeproj/xcshareddata/xcschemes/VideoCaching.xcscheme new file mode 100644 index 00000000..8e897858 --- /dev/null +++ b/examples/video-caching/ios/VideoCaching.xcodeproj/xcshareddata/xcschemes/VideoCaching.xcscheme @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/video-caching/ios/VideoCaching/AppDelegate.h b/examples/video-caching/ios/VideoCaching/AppDelegate.h new file mode 100644 index 00000000..a9654d5e --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/AppDelegate.h @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +@interface AppDelegate : UIResponder + +@property (nonatomic, strong) UIWindow *window; + +@end diff --git a/examples/video-caching/ios/VideoCaching/AppDelegate.m b/examples/video-caching/ios/VideoCaching/AppDelegate.m new file mode 100644 index 00000000..eebb5cef --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/AppDelegate.m @@ -0,0 +1,37 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import "AppDelegate.h" + +#import +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + NSURL *jsCodeLocation; + + jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; + + RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation + moduleName:@"VideoCaching" + initialProperties:nil + launchOptions:launchOptions]; + rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; + + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + UIViewController *rootViewController = [UIViewController new]; + rootViewController.view = rootView; + self.window.rootViewController = rootViewController; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/examples/video-caching/ios/VideoCaching/Base.lproj/LaunchScreen.xib b/examples/video-caching/ios/VideoCaching/Base.lproj/LaunchScreen.xib new file mode 100644 index 00000000..4defeec6 --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/Base.lproj/LaunchScreen.xib @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/examples/video-caching/ios/VideoCaching/Images.xcassets/AppIcon.appiconset/Contents.json b/examples/video-caching/ios/VideoCaching/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..19882d56 --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/examples/video-caching/ios/VideoCaching/Images.xcassets/Contents.json b/examples/video-caching/ios/VideoCaching/Images.xcassets/Contents.json new file mode 100644 index 00000000..2d92bd53 --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/examples/video-caching/ios/VideoCaching/Info.plist b/examples/video-caching/ios/VideoCaching/Info.plist new file mode 100644 index 00000000..171fecca --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/Info.plist @@ -0,0 +1,56 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + VideoCaching + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + NSLocationWhenInUseUsageDescription + + NSAppTransportSecurity + + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + + diff --git a/examples/video-caching/ios/VideoCaching/main.m b/examples/video-caching/ios/VideoCaching/main.m new file mode 100644 index 00000000..3d767fcb --- /dev/null +++ b/examples/video-caching/ios/VideoCaching/main.m @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import + +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/examples/video-caching/ios/VideoCachingTests/Info.plist b/examples/video-caching/ios/VideoCachingTests/Info.plist new file mode 100644 index 00000000..886825cc --- /dev/null +++ b/examples/video-caching/ios/VideoCachingTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/examples/video-caching/ios/VideoCachingTests/VideoCachingTests.m b/examples/video-caching/ios/VideoCachingTests/VideoCachingTests.m new file mode 100644 index 00000000..4611ed3a --- /dev/null +++ b/examples/video-caching/ios/VideoCachingTests/VideoCachingTests.m @@ -0,0 +1,70 @@ +/** + * Copyright (c) 2015-present, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React Native!" + +@interface VideoCachingTests : XCTestCase + +@end + +@implementation VideoCachingTests + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; + RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + + RCTSetLogFunction(RCTDefaultLogFunction); + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + + +@end diff --git a/examples/video-caching/package.json b/examples/video-caching/package.json new file mode 100644 index 00000000..be615106 --- /dev/null +++ b/examples/video-caching/package.json @@ -0,0 +1,24 @@ +{ + "name": "VideoCaching", + "version": "0.0.1", + "private": true, + "scripts": { + "start": "node node_modules/react-native/local-cli/cli.js start", + "test": "jest" + }, + "dependencies": { + "react": "16.4.1", + "react-native": "0.56.0", + "react-native-video": "file:../.." + }, + "devDependencies": { + "babel-jest": "22.4.1", + "babel-preset-react-native": "5.0.2", + "express": "^4.16.2", + "jest": "22.4.2", + "react-test-renderer": "16.2.0" + }, + "jest": { + "preset": "react-native" + } +} diff --git a/examples/video-caching/rn-cli.config.js b/examples/video-caching/rn-cli.config.js new file mode 100644 index 00000000..0a4bdae1 --- /dev/null +++ b/examples/video-caching/rn-cli.config.js @@ -0,0 +1,7 @@ +const blacklist = require('metro').createBlacklist; + +module.exports = { + getBlacklistRE: function() { + return blacklist([/node_modules\/react-native-video\/examples\/.*/]); + } +}; \ No newline at end of file diff --git a/examples/video-caching/update.sh b/examples/video-caching/update.sh new file mode 100755 index 00000000..cfd09c31 --- /dev/null +++ b/examples/video-caching/update.sh @@ -0,0 +1,5 @@ +rm -rf node_modules +yarn +cd ios +rm -rf Pods/* Podfile.lock +pod install diff --git a/ios/RCTVideo.xcodeproj/project.pbxproj b/ios/RCTVideo.xcodeproj/project.pbxproj index a2525e94..4e675ac7 100644 --- a/ios/RCTVideo.xcodeproj/project.pbxproj +++ b/ios/RCTVideo.xcodeproj/project.pbxproj @@ -7,14 +7,14 @@ objects = { /* Begin PBXBuildFile section */ - 31CAFB211CADA8CD009BCF6F /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */; }; - 31CAFB2F1CADC77F009BCF6F /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */; }; - 641E284D1F0EECF100443AF6 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */; }; - 641E284E1F0EECF100443AF6 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */; }; - 641E284F1F0EECF100443AF6 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD49E3C1AC8DEF000610F8E /* RCTVideoManager.m */; }; - 641E28501F0EECF100443AF6 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */; }; - BBD49E3F1AC8DEF000610F8E /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */; }; - BBD49E401AC8DEF000610F8E /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = BBD49E3C1AC8DEF000610F8E /* RCTVideoManager.m */; }; + D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; }; + D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C032110259000073188 /* UIView+FindUIViewController.m */; }; + D1107C0C2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; }; + D1107C0D2110259000073188 /* RCTVideo.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C052110259000073188 /* RCTVideo.m */; }; + D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C062110259000073188 /* RCTVideoManager.m */; }; + D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C062110259000073188 /* RCTVideoManager.m */; }; + D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C082110259000073188 /* RCTVideoPlayerViewController.m */; }; + D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = D1107C082110259000073188 /* RCTVideoPlayerViewController.m */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -40,16 +40,16 @@ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 31CAFB1F1CADA8CD009BCF6F /* UIView+FindUIViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIView+FindUIViewController.h"; sourceTree = ""; }; - 31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIView+FindUIViewController.m"; sourceTree = ""; }; - 31CAFB2D1CADC77F009BCF6F /* RCTVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideoPlayerViewController.h; sourceTree = ""; }; - 31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVideoPlayerViewController.m; sourceTree = ""; }; - 31CAFB301CAE6B5F009BCF6F /* RCTVideoPlayerViewControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideoPlayerViewControllerDelegate.h; sourceTree = ""; }; 641E28441F0EEC8500443AF6 /* libRCTVideo.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTVideo.a; sourceTree = BUILT_PRODUCTS_DIR; }; - BBD49E391AC8DEF000610F8E /* RCTVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideo.h; sourceTree = ""; }; - BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVideo.m; sourceTree = ""; }; - BBD49E3B1AC8DEF000610F8E /* RCTVideoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTVideoManager.h; sourceTree = ""; }; - BBD49E3C1AC8DEF000610F8E /* RCTVideoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTVideoManager.m; sourceTree = ""; }; + D1107C012110259000073188 /* RCTVideoPlayerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewController.h; path = Video/RCTVideoPlayerViewController.h; sourceTree = ""; }; + D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoPlayerViewControllerDelegate.h; path = Video/RCTVideoPlayerViewControllerDelegate.h; sourceTree = ""; }; + D1107C032110259000073188 /* UIView+FindUIViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIView+FindUIViewController.m"; path = "Video/UIView+FindUIViewController.m"; sourceTree = ""; }; + D1107C042110259000073188 /* UIView+FindUIViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIView+FindUIViewController.h"; path = "Video/UIView+FindUIViewController.h"; sourceTree = ""; }; + D1107C052110259000073188 /* RCTVideo.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideo.m; path = Video/RCTVideo.m; sourceTree = ""; }; + D1107C062110259000073188 /* RCTVideoManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoManager.m; path = Video/RCTVideoManager.m; sourceTree = ""; }; + D1107C072110259000073188 /* RCTVideo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideo.h; path = Video/RCTVideo.h; sourceTree = ""; }; + D1107C082110259000073188 /* RCTVideoPlayerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = RCTVideoPlayerViewController.m; path = Video/RCTVideoPlayerViewController.m; sourceTree = ""; }; + D1107C092110259000073188 /* RCTVideoManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RCTVideoManager.h; path = Video/RCTVideoManager.h; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -78,20 +78,28 @@ name = Products; sourceTree = ""; }; + 49E995712048B4CE00EA7890 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - BBD49E391AC8DEF000610F8E /* RCTVideo.h */, - BBD49E3A1AC8DEF000610F8E /* RCTVideo.m */, - 31CAFB301CAE6B5F009BCF6F /* RCTVideoPlayerViewControllerDelegate.h */, - 31CAFB2D1CADC77F009BCF6F /* RCTVideoPlayerViewController.h */, - 31CAFB2E1CADC77F009BCF6F /* RCTVideoPlayerViewController.m */, - BBD49E3B1AC8DEF000610F8E /* RCTVideoManager.h */, - BBD49E3C1AC8DEF000610F8E /* RCTVideoManager.m */, - 31CAFB1F1CADA8CD009BCF6F /* UIView+FindUIViewController.h */, - 31CAFB201CADA8CD009BCF6F /* UIView+FindUIViewController.m */, + D1107C072110259000073188 /* RCTVideo.h */, + D1107C052110259000073188 /* RCTVideo.m */, + D1107C092110259000073188 /* RCTVideoManager.h */, + D1107C062110259000073188 /* RCTVideoManager.m */, + D1107C012110259000073188 /* RCTVideoPlayerViewController.h */, + D1107C082110259000073188 /* RCTVideoPlayerViewController.m */, + D1107C022110259000073188 /* RCTVideoPlayerViewControllerDelegate.h */, + D1107C042110259000073188 /* UIView+FindUIViewController.h */, + D1107C032110259000073188 /* UIView+FindUIViewController.m */, 134814211AA4EA7D00B7C361 /* Products */, 641E28441F0EEC8500443AF6 /* libRCTVideo.a */, + 49E995712048B4CE00EA7890 /* Frameworks */, ); sourceTree = ""; }; @@ -173,10 +181,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 31CAFB211CADA8CD009BCF6F /* UIView+FindUIViewController.m in Sources */, - 31CAFB2F1CADC77F009BCF6F /* RCTVideoPlayerViewController.m in Sources */, - BBD49E3F1AC8DEF000610F8E /* RCTVideo.m in Sources */, - BBD49E401AC8DEF000610F8E /* RCTVideoManager.m in Sources */, + D1107C0A2110259000073188 /* UIView+FindUIViewController.m in Sources */, + D1107C102110259000073188 /* RCTVideoPlayerViewController.m in Sources */, + D1107C0E2110259000073188 /* RCTVideoManager.m in Sources */, + D1107C0C2110259000073188 /* RCTVideo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -184,10 +192,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 641E284D1F0EECF100443AF6 /* RCTVideo.m in Sources */, - 641E284E1F0EECF100443AF6 /* RCTVideoPlayerViewController.m in Sources */, - 641E284F1F0EECF100443AF6 /* RCTVideoManager.m in Sources */, - 641E28501F0EECF100443AF6 /* UIView+FindUIViewController.m in Sources */, + D1107C0B2110259000073188 /* UIView+FindUIViewController.m in Sources */, + D1107C112110259000073188 /* RCTVideoPlayerViewController.m in Sources */, + D1107C0F2110259000073188 /* RCTVideoManager.m in Sources */, + D1107C0D2110259000073188 /* RCTVideo.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -275,6 +283,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/Vendor/SPTPersistentCache/include/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; @@ -289,6 +298,7 @@ HEADER_SEARCH_PATHS = ( "$(inherited)", /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, + "$(SRCROOT)/Vendor/SPTPersistentCache/include/**", ); LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = "-ObjC"; @@ -362,6 +372,7 @@ 641E284B1F0EEC8500443AF6 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/ios/RCTVideo.h b/ios/Video/RCTVideo.h similarity index 82% rename from ios/RCTVideo.h rename to ios/Video/RCTVideo.h index 1c471236..b2296c5d 100644 --- a/ios/RCTVideo.h +++ b/ios/Video/RCTVideo.h @@ -5,9 +5,18 @@ #import "RCTVideoPlayerViewController.h" #import "RCTVideoPlayerViewControllerDelegate.h" -@class RCTEventDispatcher; +#if __has_include() +#import +#import +#import +#endif +@class RCTEventDispatcher; +#if __has_include() +@interface RCTVideo : UIView +#else @interface RCTVideo : UIView +#endif @property (nonatomic, copy) RCTBubblingEventBlock onVideoLoadStart; @property (nonatomic, copy) RCTBubblingEventBlock onVideoLoad; diff --git a/ios/RCTVideo.m b/ios/Video/RCTVideo.m similarity index 75% rename from ios/RCTVideo.m rename to ios/Video/RCTVideo.m index f5fc6125..9fe30b63 100644 --- a/ios/RCTVideo.m +++ b/ios/Video/RCTVideo.m @@ -15,6 +15,12 @@ static NSString *const timedMetadata = @"timedMetadata"; static int const RCTVideoUnset = -1; +#ifdef DEBUG + #define DebugLog(...) NSLog(__VA_ARGS__) +#else + #define DebugLog(...) (void)0 +#endif + @implementation RCTVideo { AVPlayer *_player; @@ -25,7 +31,7 @@ static int const RCTVideoUnset = -1; BOOL _playerLayerObserverSet; AVPlayerViewController *_playerViewController; NSURL *_videoURL; - + /* Required to publish events */ RCTEventDispatcher *_eventDispatcher; BOOL _playbackRateObserverRegistered; @@ -34,12 +40,12 @@ static int const RCTVideoUnset = -1; bool _pendingSeek; float _pendingSeekTime; float _lastSeekTime; - + /* For sending videoProgress events */ Float64 _progressUpdateInterval; BOOL _controls; id _timeObserver; - + /* Keep track of any modifiers, need to be applied after each play */ float _volume; float _rate; @@ -57,13 +63,16 @@ static int const RCTVideoUnset = -1; NSString * _resizeMode; BOOL _fullscreenPlayerPresented; UIViewController * _presentingViewController; +#if __has_include() + RCTVideoCache * _videoCache; +#endif } - (instancetype)initWithEventDispatcher:(RCTEventDispatcher *)eventDispatcher { if ((self = [super init])) { _eventDispatcher = eventDispatcher; - + _playbackRateObserverRegistered = NO; _playbackStalled = NO; _rate = 1.0; @@ -79,17 +88,19 @@ static int const RCTVideoUnset = -1; _allowsExternalPlayback = YES; _playWhenInactive = false; _ignoreSilentSwitch = @"inherit"; // inherit, ignore, obey - +#if __has_include() + _videoCache = [RCTVideoCache sharedInstance]; +#endif [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillResignActive:) name:UIApplicationWillResignActiveNotification object:nil]; - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; - + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification @@ -100,7 +111,7 @@ static int const RCTVideoUnset = -1; name:AVAudioSessionRouteChangeNotification object:nil]; } - + return self; } @@ -120,24 +131,24 @@ static int const RCTVideoUnset = -1; - (CMTime)playerItemDuration { - AVPlayerItem *playerItem = [_player currentItem]; - if (playerItem.status == AVPlayerItemStatusReadyToPlay) - { - return([playerItem duration]); - } - - return(kCMTimeInvalid); + AVPlayerItem *playerItem = [_player currentItem]; + if (playerItem.status == AVPlayerItemStatusReadyToPlay) + { + return([playerItem duration]); + } + + return(kCMTimeInvalid); } - (CMTimeRange)playerItemSeekableTimeRange { - AVPlayerItem *playerItem = [_player currentItem]; - if (playerItem.status == AVPlayerItemStatusReadyToPlay) - { - return [playerItem seekableTimeRanges].firstObject.CMTimeRangeValue; - } - - return (kCMTimeRangeZero); + AVPlayerItem *playerItem = [_player currentItem]; + if (playerItem.status == AVPlayerItemStatusReadyToPlay) + { + return [playerItem seekableTimeRanges].firstObject.CMTimeRangeValue; + } + + return (kCMTimeRangeZero); } -(void)addPlayerTimeObserver @@ -155,11 +166,11 @@ static int const RCTVideoUnset = -1; /* Cancels the previously registered time observer. */ -(void)removePlayerTimeObserver { - if (_timeObserver) - { - [_player removeTimeObserver:_timeObserver]; - _timeObserver = nil; - } + if (_timeObserver) + { + [_player removeTimeObserver:_timeObserver]; + _timeObserver = nil; + } } #pragma mark - Progress @@ -177,7 +188,7 @@ static int const RCTVideoUnset = -1; - (void)applicationWillResignActive:(NSNotification *)notification { if (_playInBackground || _playWhenInactive || _paused) return; - + [_player pause]; [_player setRate:0.0]; } @@ -213,32 +224,32 @@ static int const RCTVideoUnset = -1; - (void)sendProgressUpdate { - AVPlayerItem *video = [_player currentItem]; - if (video == nil || video.status != AVPlayerItemStatusReadyToPlay) { - return; - } - - CMTime playerDuration = [self playerItemDuration]; - if (CMTIME_IS_INVALID(playerDuration)) { - return; - } - - CMTime currentTime = _player.currentTime; - const Float64 duration = CMTimeGetSeconds(playerDuration); - const Float64 currentTimeSecs = CMTimeGetSeconds(currentTime); - - [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTVideo_progress" object:nil userInfo:@{@"progress": [NSNumber numberWithDouble: currentTimeSecs / duration]}]; - - if( currentTimeSecs >= 0 && self.onVideoProgress) { - self.onVideoProgress(@{ - @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)], - @"playableDuration": [self calculatePlayableDuration], - @"atValue": [NSNumber numberWithLongLong:currentTime.value], - @"atTimescale": [NSNumber numberWithInt:currentTime.timescale], - @"target": self.reactTag, - @"seekableDuration": [self calculateSeekableDuration], - }); - } + AVPlayerItem *video = [_player currentItem]; + if (video == nil || video.status != AVPlayerItemStatusReadyToPlay) { + return; + } + + CMTime playerDuration = [self playerItemDuration]; + if (CMTIME_IS_INVALID(playerDuration)) { + return; + } + + CMTime currentTime = _player.currentTime; + const Float64 duration = CMTimeGetSeconds(playerDuration); + const Float64 currentTimeSecs = CMTimeGetSeconds(currentTime); + + [[NSNotificationCenter defaultCenter] postNotificationName:@"RCTVideo_progress" object:nil userInfo:@{@"progress": [NSNumber numberWithDouble: currentTimeSecs / duration]}]; + + if( currentTimeSecs >= 0 && self.onVideoProgress) { + self.onVideoProgress(@{ + @"currentTime": [NSNumber numberWithFloat:CMTimeGetSeconds(currentTime)], + @"playableDuration": [self calculatePlayableDuration], + @"atValue": [NSNumber numberWithLongLong:currentTime.value], + @"atTimescale": [NSNumber numberWithInt:currentTime.timescale], + @"target": self.reactTag, + @"seekableDuration": [self calculateSeekableDuration], + }); + } } /*! @@ -268,12 +279,12 @@ static int const RCTVideoUnset = -1; - (NSNumber *)calculateSeekableDuration { - CMTimeRange timeRange = [self playerItemSeekableTimeRange]; - if (CMTIME_IS_NUMERIC(timeRange.duration)) - { - return [NSNumber numberWithFloat:CMTimeGetSeconds(timeRange.duration)]; - } - return [NSNumber numberWithInteger:0]; + CMTimeRange timeRange = [self playerItemSeekableTimeRange]; + if (CMTIME_IS_NUMERIC(timeRange.duration)) + { + return [NSNumber numberWithFloat:CMTimeGetSeconds(timeRange.duration)]; + } + return [NSNumber numberWithInteger:0]; } - (void)addPlayerItemObservers @@ -306,42 +317,43 @@ static int const RCTVideoUnset = -1; [self removePlayerLayer]; [self removePlayerTimeObserver]; [self removePlayerItemObservers]; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t) 0), dispatch_get_main_queue(), ^{ // perform on next run loop, otherwise other passed react-props may not be set - _playerItem = [self playerItemForSource:source]; - [self addPlayerItemObservers]; - - [_player pause]; - [_playerViewController.view removeFromSuperview]; - _playerViewController = nil; - - if (_playbackRateObserverRegistered) { - [_player removeObserver:self forKeyPath:playbackRate context:nil]; - _playbackRateObserverRegistered = NO; - } - - _player = [AVPlayer playerWithPlayerItem:_playerItem]; - _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; - - [_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; - _playbackRateObserverRegistered = YES; - - [self addPlayerTimeObserver]; - - //Perform on next run loop, otherwise onVideoLoadStart is nil - if(self.onVideoLoadStart) { - id uri = [source objectForKey:@"uri"]; - id type = [source objectForKey:@"type"]; - self.onVideoLoadStart(@{@"src": @{ + [self playerItemForSource:source withCallback:^(AVPlayerItem * playerItem) { + _playerItem = playerItem; + [self addPlayerItemObservers]; + + [_player pause]; + [_playerViewController.view removeFromSuperview]; + _playerViewController = nil; + + if (_playbackRateObserverRegistered) { + [_player removeObserver:self forKeyPath:playbackRate context:nil]; + _playbackRateObserverRegistered = NO; + } + + _player = [AVPlayer playerWithPlayerItem:_playerItem]; + _player.actionAtItemEnd = AVPlayerActionAtItemEndNone; + + [_player addObserver:self forKeyPath:playbackRate options:0 context:nil]; + _playbackRateObserverRegistered = YES; + + [self addPlayerTimeObserver]; + + //Perform on next run loop, otherwise onVideoLoadStart is nil + if (self.onVideoLoadStart) { + id uri = [source objectForKey:@"uri"]; + id type = [source objectForKey:@"type"]; + self.onVideoLoadStart(@{@"src": @{ @"uri": uri ? uri : [NSNull null], @"type": type ? type : [NSNull null], @"isNetwork": [NSNumber numberWithBool:(bool)[source objectForKey:@"isNetwork"]]}, - @"target": self.reactTag - }); - } - + @"target": self.reactTag + }); + } + }]; }); _videoLoadStarted = YES; } @@ -356,7 +368,7 @@ static int const RCTVideoUnset = -1; NSString* relativeFilePath = [filepath lastPathComponent]; // the file may be multiple levels below the documents directory NSArray* fileComponents = [filepath componentsSeparatedByString:@"Documents/"]; - if (fileComponents.count>1) { + if (fileComponents.count > 1) { relativeFilePath = [fileComponents objectAtIndex:1]; } @@ -367,37 +379,13 @@ static int const RCTVideoUnset = -1; return nil; } -- (AVPlayerItem*)playerItemForSource:(NSDictionary *)source +- (void)playerItemPrepareText:(AVAsset *)asset assetOptions:(NSDictionary * __nullable)assetOptions withCallback:(void(^)(AVPlayerItem *))handler { - bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; - bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; - NSString *uri = [source objectForKey:@"uri"]; - NSString *type = [source objectForKey:@"type"]; - - AVURLAsset *asset; - NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; - - if (isNetwork) { - /* Per #1091, this is not a public API. We need to either get approval from Apple to use this - * or use a different approach. - NSDictionary *headers = [source objectForKey:@"requestHeaders"]; - if ([headers count] > 0) { - [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; - } - */ - NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; - [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; - asset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:uri] options:assetOptions]; - } else if (isAsset) { // assets on iOS can be in the Bundle or Documents folder - asset = [AVURLAsset URLAssetWithURL:[self urlFilePath:uri] options:nil]; - } else { // file passed in through JS, or an asset in the Xcode project - asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; - } - if (!_textTracks) { - return [AVPlayerItem playerItemWithAsset:asset]; + handler([AVPlayerItem playerItemWithAsset:asset]); + return; } - + // sideload text tracks AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init]; @@ -439,9 +427,112 @@ static int const RCTVideoUnset = -1; [self setTextTracks:validTextTracks]; } - return [AVPlayerItem playerItemWithAsset:mixComposition]; + handler([AVPlayerItem playerItemWithAsset:mixComposition]); } +- (void)playerItemForSource:(NSDictionary *)source withCallback:(void(^)(AVPlayerItem *))handler +{ + bool isNetwork = [RCTConvert BOOL:[source objectForKey:@"isNetwork"]]; + bool isAsset = [RCTConvert BOOL:[source objectForKey:@"isAsset"]]; + NSString *uri = [source objectForKey:@"uri"]; + NSString *type = [source objectForKey:@"type"]; + + NSURL *url = isNetwork || isAsset + ? [NSURL URLWithString:uri] + : [[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]]; + NSMutableDictionary *assetOptions = [[NSMutableDictionary alloc] init]; + + if (isNetwork) { + /* Per #1091, this is not a public API. + * We need to either get approval from Apple to use this or use a different approach. + NSDictionary *headers = [source objectForKey:@"requestHeaders"]; + if ([headers count] > 0) { + [assetOptions setObject:headers forKey:@"AVURLAssetHTTPHeaderFieldsKey"]; + } + */ + NSArray *cookies = [[NSHTTPCookieStorage sharedHTTPCookieStorage] cookies]; + [assetOptions setObject:cookies forKey:AVURLAssetHTTPCookiesKey]; + +#if __has_include() + if (!_textTracks) { + /* The DVURLAsset created by cache doesn't have a tracksWithMediaType property, so trying + * to bring in the text track code will crash. I suspect this is because the asset hasn't fully loaded. + * Until this is fixed, we need to bypass caching when text tracks are specified. + */ + DebugLog(@"Caching is not supported for uri '%@' because text tracks are not compatible with the cache. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); + [self playerItemForSourceUsingCache:uri assetOptions:assetOptions withCallback:handler]; + return; + } +#endif + + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:assetOptions]; + [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; + return; + } else if (isAsset) { + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:nil]; + [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; + return; + } + + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:[[NSURL alloc] initFileURLWithPath:[[NSBundle mainBundle] pathForResource:uri ofType:type]] options:nil]; + [self playerItemPrepareText:asset assetOptions:assetOptions withCallback:handler]; +} + +#if __has_include() + +- (void)playerItemForSourceUsingCache:(NSString *)uri assetOptions:(NSDictionary *)options withCallback:(void(^)(AVPlayerItem *))handler { + NSURL *url = [NSURL URLWithString:uri]; + [_videoCache getItemForUri:uri withCallback:^(RCTVideoCacheStatus videoCacheStatus, AVAsset * _Nullable cachedAsset) { + switch (videoCacheStatus) { + case RCTVideoCacheStatusMissingFileExtension: { + DebugLog(@"Could not generate cache key for uri '%@'. It is currently not supported to cache urls that do not include a file extension. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; + [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; + return; + } + case RCTVideoCacheStatusUnsupportedFileExtension: { + DebugLog(@"Could not generate cache key for uri '%@'. The file extension of that uri is currently not supported. The video file will not be cached. Checkout https://github.com/react-native-community/react-native-video/blob/master/docs/caching.md", uri); + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:url options:options]; + [self playerItemPrepareText:asset assetOptions:options withCallback:handler]; + return; + } + default: + if (cachedAsset) { + DebugLog(@"Playing back uri '%@' from cache", uri); + // See note in playerItemForSource about not being able to support text tracks & caching + handler([AVPlayerItem playerItemWithAsset:asset]); + return; + } + } + + DVURLAsset *asset = [[DVURLAsset alloc] initWithURL:url options:options networkTimeout:10000]; + asset.loaderDelegate = self; + + /* More granular code to have control over the DVURLAsset + DVAssetLoaderDelegate *resourceLoaderDelegate = [[DVAssetLoaderDelegate alloc] initWithURL:url]; + resourceLoaderDelegate.delegate = self; + NSURLComponents *components = [[NSURLComponents alloc] initWithURL:url resolvingAgainstBaseURL:NO]; + components.scheme = [DVAssetLoaderDelegate scheme]; + AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:[components URL] options:options]; + [asset.resourceLoader setDelegate:resourceLoaderDelegate queue:dispatch_get_main_queue()]; + */ + + handler([AVPlayerItem playerItemWithAsset:asset]); + }]; +} + +#pragma mark - DVAssetLoaderDelegate + +- (void)dvAssetLoaderDelegate:(DVAssetLoaderDelegate *)loaderDelegate + didLoadData:(NSData *)data + forURL:(NSURL *)url { + [_videoCache storeItem:data forUri:[url absoluteString] withCallback:^(BOOL success) { + DebugLog(@"Cache data stored successfully 🎉"); + }]; +} + +#endif + - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { if (object == _playerItem) { @@ -591,9 +682,9 @@ static int const RCTVideoUnset = -1; - (void)playerItemDidReachEnd:(NSNotification *)notification { if(self.onVideoEnd) { - self.onVideoEnd(@{@"target": self.reactTag}); + self.onVideoEnd(@{@"target": self.reactTag}); } - + if (_repeat) { AVPlayerItem *item = [notification object]; [item seekToTime:kCMTimeZero]; @@ -654,7 +745,7 @@ static int const RCTVideoUnset = -1; [_player play]; [_player setRate:_rate]; } - + _paused = paused; } @@ -989,66 +1080,66 @@ static int const RCTVideoUnset = -1; - (BOOL)getFullscreen { - return _fullscreenPlayerPresented; + return _fullscreenPlayerPresented; } - (void)setFullscreen:(BOOL)fullscreen { - if( fullscreen && !_fullscreenPlayerPresented ) + if( fullscreen && !_fullscreenPlayerPresented ) + { + // Ensure player view controller is not null + if( !_playerViewController ) { - // Ensure player view controller is not null - if( !_playerViewController ) - { - [self usePlayerViewController]; - } - // Set presentation style to fullscreen - [_playerViewController setModalPresentationStyle:UIModalPresentationFullScreen]; - - // Find the nearest view controller - UIViewController *viewController = [self firstAvailableUIViewController]; - if( !viewController ) - { - UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; - viewController = keyWindow.rootViewController; - if( viewController.childViewControllers.count > 0 ) - { - viewController = viewController.childViewControllers.lastObject; - } - } - if( viewController ) - { - _presentingViewController = viewController; - if(self.onVideoFullscreenPlayerWillPresent) { - self.onVideoFullscreenPlayerWillPresent(@{@"target": self.reactTag}); - } - [viewController presentViewController:_playerViewController animated:true completion:^{ - _playerViewController.showsPlaybackControls = YES; - _fullscreenPlayerPresented = fullscreen; - if(self.onVideoFullscreenPlayerDidPresent) { - self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag}); - } - }]; - } + [self usePlayerViewController]; } - else if ( !fullscreen && _fullscreenPlayerPresented ) + // Set presentation style to fullscreen + [_playerViewController setModalPresentationStyle:UIModalPresentationFullScreen]; + + // Find the nearest view controller + UIViewController *viewController = [self firstAvailableUIViewController]; + if( !viewController ) { - [self videoPlayerViewControllerWillDismiss:_playerViewController]; - [_presentingViewController dismissViewControllerAnimated:true completion:^{ - [self videoPlayerViewControllerDidDismiss:_playerViewController]; - }]; + UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow]; + viewController = keyWindow.rootViewController; + if( viewController.childViewControllers.count > 0 ) + { + viewController = viewController.childViewControllers.lastObject; + } } + if( viewController ) + { + _presentingViewController = viewController; + if(self.onVideoFullscreenPlayerWillPresent) { + self.onVideoFullscreenPlayerWillPresent(@{@"target": self.reactTag}); + } + [viewController presentViewController:_playerViewController animated:true completion:^{ + _playerViewController.showsPlaybackControls = YES; + _fullscreenPlayerPresented = fullscreen; + if(self.onVideoFullscreenPlayerDidPresent) { + self.onVideoFullscreenPlayerDidPresent(@{@"target": self.reactTag}); + } + }]; + } + } + else if ( !fullscreen && _fullscreenPlayerPresented ) + { + [self videoPlayerViewControllerWillDismiss:_playerViewController]; + [_presentingViewController dismissViewControllerAnimated:true completion:^{ + [self videoPlayerViewControllerDidDismiss:_playerViewController]; + }]; + } } - (void)usePlayerViewController { - if( _player ) - { - _playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem]; - // to prevent video from being animated when resizeMode is 'cover' - // resize mode must be set before subview is added - [self setResizeMode:_resizeMode]; - [self addSubview:_playerViewController.view]; - } + if( _player ) + { + _playerViewController = [self createPlayerViewController:_player withPlayerItem:_playerItem]; + // to prevent video from being animated when resizeMode is 'cover' + // resize mode must be set before subview is added + [self setResizeMode:_resizeMode]; + [self addSubview:_playerViewController.view]; + } } - (void)usePlayerLayer @@ -1072,21 +1163,21 @@ static int const RCTVideoUnset = -1; - (void)setControls:(BOOL)controls { - if( _controls != controls || (!_playerLayer && !_playerViewController) ) + if( _controls != controls || (!_playerLayer && !_playerViewController) ) + { + _controls = controls; + if( _controls ) { - _controls = controls; - if( _controls ) - { - [self removePlayerLayer]; - [self usePlayerViewController]; - } - else - { - [_playerViewController.view removeFromSuperview]; - _playerViewController = nil; - [self usePlayerLayer]; - } + [self removePlayerLayer]; + [self usePlayerViewController]; } + else + { + [_playerViewController.view removeFromSuperview]; + _playerViewController = nil; + [self usePlayerLayer]; + } + } } - (void)setProgressUpdateInterval:(float)progressUpdateInterval @@ -1113,24 +1204,24 @@ static int const RCTVideoUnset = -1; - (void)videoPlayerViewControllerWillDismiss:(AVPlayerViewController *)playerViewController { - if (_playerViewController == playerViewController && _fullscreenPlayerPresented && self.onVideoFullscreenPlayerWillDismiss) - { - self.onVideoFullscreenPlayerWillDismiss(@{@"target": self.reactTag}); - } + if (_playerViewController == playerViewController && _fullscreenPlayerPresented && self.onVideoFullscreenPlayerWillDismiss) + { + self.onVideoFullscreenPlayerWillDismiss(@{@"target": self.reactTag}); + } } - (void)videoPlayerViewControllerDidDismiss:(AVPlayerViewController *)playerViewController { - if (_playerViewController == playerViewController && _fullscreenPlayerPresented) - { - _fullscreenPlayerPresented = false; - _presentingViewController = nil; - _playerViewController = nil; - [self applyModifiers]; - if(self.onVideoFullscreenPlayerDidDismiss) { - self.onVideoFullscreenPlayerDidDismiss(@{@"target": self.reactTag}); - } + if (_playerViewController == playerViewController && _fullscreenPlayerPresented) + { + _fullscreenPlayerPresented = false; + _presentingViewController = nil; + _playerViewController = nil; + [self applyModifiers]; + if(self.onVideoFullscreenPlayerDidDismiss) { + self.onVideoFullscreenPlayerDidDismiss(@{@"target": self.reactTag}); } + } } #pragma mark - React View Management @@ -1143,15 +1234,15 @@ static int const RCTVideoUnset = -1; { [self setControls:true]; } - + if( _controls ) { - view.frame = self.bounds; - [_playerViewController.contentOverlayView insertSubview:view atIndex:atIndex]; + view.frame = self.bounds; + [_playerViewController.contentOverlayView insertSubview:view atIndex:atIndex]; } else { - RCTLogError(@"video cannot have any subviews"); + RCTLogError(@"video cannot have any subviews"); } return; } @@ -1160,7 +1251,7 @@ static int const RCTVideoUnset = -1; { if( _controls ) { - [subview removeFromSuperview]; + [subview removeFromSuperview]; } else { @@ -1175,7 +1266,7 @@ static int const RCTVideoUnset = -1; if( _controls ) { _playerViewController.view.frame = self.bounds; - + // also adjust all subviews of contentOverlayView for (UIView* subview in _playerViewController.contentOverlayView.subviews) { subview.frame = self.bounds; @@ -1183,10 +1274,10 @@ static int const RCTVideoUnset = -1; } else { - [CATransaction begin]; - [CATransaction setAnimationDuration:0]; - _playerLayer.frame = self.bounds; - [CATransaction commit]; + [CATransaction begin]; + [CATransaction setAnimationDuration:0]; + _playerLayer.frame = self.bounds; + [CATransaction commit]; } } @@ -1200,18 +1291,18 @@ static int const RCTVideoUnset = -1; _playbackRateObserverRegistered = NO; } _player = nil; - + [self removePlayerLayer]; - + [_playerViewController.view removeFromSuperview]; _playerViewController = nil; - + [self removePlayerTimeObserver]; [self removePlayerItemObservers]; - + _eventDispatcher = nil; [[NSNotificationCenter defaultCenter] removeObserver:self]; - + [super removeFromSuperview]; } diff --git a/ios/RCTVideoManager.h b/ios/Video/RCTVideoManager.h similarity index 100% rename from ios/RCTVideoManager.h rename to ios/Video/RCTVideoManager.h diff --git a/ios/RCTVideoManager.m b/ios/Video/RCTVideoManager.m similarity index 100% rename from ios/RCTVideoManager.m rename to ios/Video/RCTVideoManager.m diff --git a/ios/RCTVideoPlayerViewController.h b/ios/Video/RCTVideoPlayerViewController.h similarity index 100% rename from ios/RCTVideoPlayerViewController.h rename to ios/Video/RCTVideoPlayerViewController.h diff --git a/ios/RCTVideoPlayerViewController.m b/ios/Video/RCTVideoPlayerViewController.m similarity index 100% rename from ios/RCTVideoPlayerViewController.m rename to ios/Video/RCTVideoPlayerViewController.m diff --git a/ios/RCTVideoPlayerViewControllerDelegate.h b/ios/Video/RCTVideoPlayerViewControllerDelegate.h similarity index 100% rename from ios/RCTVideoPlayerViewControllerDelegate.h rename to ios/Video/RCTVideoPlayerViewControllerDelegate.h diff --git a/ios/UIView+FindUIViewController.h b/ios/Video/UIView+FindUIViewController.h similarity index 100% rename from ios/UIView+FindUIViewController.h rename to ios/Video/UIView+FindUIViewController.h diff --git a/ios/UIView+FindUIViewController.m b/ios/Video/UIView+FindUIViewController.m similarity index 100% rename from ios/UIView+FindUIViewController.m rename to ios/Video/UIView+FindUIViewController.m diff --git a/ios/VideoCaching/RCTVideoCache.h b/ios/VideoCaching/RCTVideoCache.h new file mode 100644 index 00000000..bad999ff --- /dev/null +++ b/ios/VideoCaching/RCTVideoCache.h @@ -0,0 +1,38 @@ +#import +#import +#import +#import +#import + +typedef NS_ENUM(NSUInteger, RCTVideoCacheStatus) { + RCTVideoCacheStatusMissingFileExtension, + RCTVideoCacheStatusUnsupportedFileExtension, + RCTVideoCacheStatusNotAvailable, + RCTVideoCacheStatusAvailable +}; + +@class SPTPersistentCache; +@class SPTPersistentCacheOptions; + +@interface RCTVideoCache : NSObject +{ + SPTPersistentCache *videoCache; + NSString * _Nullable cachePath; + NSString * temporaryCachePath; + NSString * _Nullable cacheIdentifier; +} + +@property(nonatomic, strong) SPTPersistentCache * _Nullable videoCache; +@property(nonatomic, strong) NSString * cachePath; +@property(nonatomic, strong) NSString * cacheIdentifier; +@property(nonatomic, strong) NSString * temporaryCachePath; + ++ (RCTVideoCache *)sharedInstance; +- (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BOOL))handler; +- (void)getItemForUri:(NSString *)url withCallback:(void(^)(RCTVideoCacheStatus, AVAsset * _Nullable)) handler; +- (NSURL *)createUniqueTemporaryFileUrl:(NSString * _Nonnull)url withExtension:(NSString * _Nonnull) extension; +- (AVURLAsset *)getItemFromTemporaryStorage:(NSString *)key; +- (BOOL)saveDataToTemporaryStorage:(NSData *)data key:(NSString *)key; +- (void) createTemporaryPath; + +@end diff --git a/ios/VideoCaching/RCTVideoCache.m b/ios/VideoCaching/RCTVideoCache.m new file mode 100644 index 00000000..1a2b83a5 --- /dev/null +++ b/ios/VideoCaching/RCTVideoCache.m @@ -0,0 +1,174 @@ +#import "RCTVideoCache.h" + +@implementation RCTVideoCache + +@synthesize videoCache; +@synthesize cachePath; +@synthesize cacheIdentifier; +@synthesize temporaryCachePath; + ++ (RCTVideoCache *)sharedInstance { + static RCTVideoCache *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] init]; + }); + return sharedInstance; +} + +- (id)init { + if (self = [super init]) { + self.cacheIdentifier = @"rct.video.cache"; + self.temporaryCachePath = [NSTemporaryDirectory() stringByAppendingPathComponent:self.cacheIdentifier]; + self.cachePath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:self.cacheIdentifier]; + SPTPersistentCacheOptions *options = [SPTPersistentCacheOptions new]; + options.cachePath = self.cachePath; + options.cacheIdentifier = self.cacheIdentifier; + options.defaultExpirationPeriod = 60 * 60 * 24 * 30; + options.garbageCollectionInterval = (NSUInteger)(1.5 * SPTPersistentCacheDefaultGCIntervalSec); + options.sizeConstraintBytes = 1024 * 1024 * 100; + options.useDirectorySeparation = NO; +#ifdef DEBUG + options.debugOutput = ^(NSString *string) { + NSLog(@"Video Cache: %@", string); + }; +#endif + [self createTemporaryPath]; + self.videoCache = [[SPTPersistentCache alloc] initWithOptions:options]; + [self.videoCache scheduleGarbageCollector]; + } + return self; +} + +- (void) createTemporaryPath { + NSError *error = nil; + BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:self.temporaryCachePath + withIntermediateDirectories:YES + attributes:nil + error:&error]; +#ifdef DEBUG + if (!success || error) { + NSLog(@"Error while! %@", error); + } +#endif +} + +- (void)storeItem:(NSData *)data forUri:(NSString *)uri withCallback:(void(^)(BOOL))handler; +{ + NSString *key = [self generateCacheKeyForUri:uri]; + if (key == nil) { + handler(NO); + return; + } + [self saveDataToTemporaryStorage:data key:key]; + [self.videoCache storeData:data forKey:key locked:NO withCallback:^(SPTPersistentCacheResponse * _Nonnull response) { + if (response.error) { +#ifdef DEBUG + NSLog(@"An error occured while saving the video into the cache: %@", [response.error localizedDescription]); +#endif + handler(NO); + return; + } + handler(YES); + } onQueue:dispatch_get_main_queue()]; + return; +} + +- (AVURLAsset *)getItemFromTemporaryStorage:(NSString *)key { + NSString * temporaryFilePath = [self.temporaryCachePath stringByAppendingPathComponent:key]; + + BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:temporaryFilePath]; + if (!fileExists) { + return nil; + } + NSURL *assetUrl = [[NSURL alloc] initFileURLWithPath:temporaryFilePath]; + AVURLAsset *asset = [AVURLAsset URLAssetWithURL:assetUrl options:nil]; + return asset; +} + +- (BOOL)saveDataToTemporaryStorage:(NSData *)data key:(NSString *)key { + NSString *temporaryFilePath = [self.temporaryCachePath stringByAppendingPathComponent:key]; + [data writeToFile:temporaryFilePath atomically:YES]; + return YES; +} + +- (NSString *)generateCacheKeyForUri:(NSString *)uri { + NSString *uriWithoutQueryParams = uri; + + // parse file extension + if ([uri rangeOfString:@"?"].location != NSNotFound) { + NSArray * components = [uri componentsSeparatedByString:@"?"]; + uriWithoutQueryParams = [components objectAtIndex:0]; + } + + NSString * pathExtension = [uriWithoutQueryParams pathExtension]; + NSArray * supportedExtensions = @[@"m4v", @"mp4", @"mov"]; + if ([pathExtension isEqualToString:@""]) { + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Missing file extension.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Missing file extension.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Missing file extension.", nil) + }; + NSError *error = [NSError errorWithDomain:@"RCTVideoCache" + code:RCTVideoCacheStatusMissingFileExtension userInfo:userInfo]; + @throw error; + } else if (![supportedExtensions containsObject:pathExtension]) { + // Notably, we don't currently support m3u8 (HLS playlists) + NSDictionary *userInfo = @{ + NSLocalizedDescriptionKey: NSLocalizedString(@"Unsupported file extension.", nil), + NSLocalizedFailureReasonErrorKey: NSLocalizedString(@"Unsupported file extension.", nil), + NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Unsupported file extension.", nil) + }; + NSError *error = [NSError errorWithDomain:@"RCTVideoCache" + code:RCTVideoCacheStatusUnsupportedFileExtension userInfo:userInfo]; + @throw error; + } + return [[self generateHashForUrl:uri] stringByAppendingPathExtension:pathExtension]; +} + +- (void)getItemForUri:(NSString *)uri withCallback:(void(^)(RCTVideoCacheStatus, AVAsset * _Nullable)) handler { + @try { + NSString *key = [self generateCacheKeyForUri:uri]; + AVURLAsset * temporaryAsset = [self getItemFromTemporaryStorage:key]; + if (temporaryAsset != nil) { + handler(RCTVideoCacheStatusAvailable, temporaryAsset); + return; + } + + [self.videoCache loadDataForKey:key withCallback:^(SPTPersistentCacheResponse * _Nonnull response) { + if (response.record == nil || response.record.data == nil) { + handler(RCTVideoCacheStatusNotAvailable, nil); + return; + } + [self saveDataToTemporaryStorage:response.record.data key:key]; + handler(RCTVideoCacheStatusAvailable, [self getItemFromTemporaryStorage:key]); + } onQueue:dispatch_get_main_queue()]; + } @catch (NSError * err) { + switch (err.code) { + case RCTVideoCacheStatusMissingFileExtension: + handler(RCTVideoCacheStatusMissingFileExtension, nil); + return; + case RCTVideoCacheStatusUnsupportedFileExtension: + handler(RCTVideoCacheStatusUnsupportedFileExtension, nil); + return; + default: + @throw err; + } + } +} + +- (NSString *)generateHashForUrl:(NSString *)string { + const char *cStr = [string UTF8String]; + unsigned char result[CC_MD5_DIGEST_LENGTH]; + CC_MD5( cStr, (CC_LONG)strlen(cStr), result ); + + return [NSString stringWithFormat: + @"%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", + result[0], result[1], result[2], result[3], + result[4], result[5], result[6], result[7], + result[8], result[9], result[10], result[11], + result[12], result[13], result[14], result[15] + ]; +} + +@end diff --git a/package.json b/package.json index 4f0dc111..3051b567 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,6 @@ "url": "git@github.com:brentvatne/react-native-video.git" }, "devDependencies": { - "jest-cli": "0.2.1", "eslint": "1.10.3", "babel-eslint": "5.0.0-beta8", "eslint-plugin-react": "3.16.1", @@ -46,4 +45,4 @@ "sourceDir": "./android-exoplayer" } } -} +} \ No newline at end of file diff --git a/react-native-video.podspec b/react-native-video.podspec index 5edb6569..c5e09c11 100644 --- a/react-native-video.podspec +++ b/react-native-video.podspec @@ -3,21 +3,33 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) Pod::Spec.new do |s| - s.name = "react-native-video" - s.version = package["version"] - s.summary = "A