Repository: olofd/react-native-photos-framework Branch: master Commit: adaa91d8bd13 Files: 333 Total size: 1.1 MB Directory structure: gitextract_pkp7bq6l/ ├── .gitignore ├── .travis.yml ├── .vscode/ │ └── launch.json ├── .watchmanconfig ├── LICENSE ├── Makefile ├── README.md ├── __tests__/ │ └── change-observer-handler.test.js ├── event-emitter.js ├── example/ │ ├── .babelrc │ ├── .buckconfig │ ├── .flowconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .npmignore │ ├── .vscode/ │ │ ├── launch.json │ │ ├── launchReactNative.js │ │ └── typings/ │ │ ├── react/ │ │ │ ├── react-addons-create-fragment.d.ts │ │ │ ├── react-addons-css-transition-group.d.ts │ │ │ ├── react-addons-linked-state-mixin.d.ts │ │ │ ├── react-addons-perf.d.ts │ │ │ ├── react-addons-pure-render-mixin.d.ts │ │ │ ├── react-addons-test-utils.d.ts │ │ │ ├── react-addons-transition-group.d.ts │ │ │ ├── react-addons-update.d.ts │ │ │ ├── react-dom.d.ts │ │ │ ├── react-global.d.ts │ │ │ └── react.d.ts │ │ └── react-native/ │ │ └── react-native.d.ts │ ├── .watchmanconfig │ ├── README.md │ ├── album-list.js │ ├── android/ │ │ ├── app/ │ │ │ ├── BUCK │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.pro │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── example/ │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res/ │ │ │ └── values/ │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle/ │ │ │ └── wrapper/ │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradle.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── keystores/ │ │ │ ├── BUCK │ │ │ └── debug.keystore.properties │ │ └── settings.gradle │ ├── index.android.js │ ├── index.ios-ajax.js │ ├── index.ios.js │ ├── ios/ │ │ ├── Example/ │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── Base.lproj/ │ │ │ │ └── LaunchScreen.xib │ │ │ ├── Images.xcassets/ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Info.plist │ │ │ └── main.m │ │ ├── Example.xcodeproj/ │ │ │ ├── project.pbxproj │ │ │ └── xcshareddata/ │ │ │ └── xcschemes/ │ │ │ └── Example.xcscheme │ │ └── ExampleTests/ │ │ ├── ExampleTests.m │ │ └── Info.plist │ ├── library-test.js │ ├── package.json │ ├── react-native-camera-roll-picker/ │ │ ├── ImageItem.js │ │ ├── camera-roll-picker.js │ │ └── index.ios.js │ ├── react-native-photos-framework/ │ │ ├── event-emitter.js │ │ └── index.js │ └── tsconfig.json ├── index.js ├── install.js ├── ios/ │ ├── RNPhotosFramework/ │ │ ├── ImageHelpers.h │ │ ├── ImageHelpers.m │ │ ├── PHAssetWithCollectionIndex.h │ │ ├── PHAssetWithCollectionIndex.m │ │ ├── PHAssetsService.h │ │ ├── PHAssetsService.m │ │ ├── PHCache.h │ │ ├── PHCache.m │ │ ├── PHCachedFetchResult.h │ │ ├── PHCachedFetchResult.m │ │ ├── PHCachingImageManagerInstance.h │ │ ├── PHCachingImageManagerInstance.m │ │ ├── PHCancellationToken.h │ │ ├── PHCancellationToken.m │ │ ├── PHCancellationTokenManager.h │ │ ├── PHCancellationTokenManager.m │ │ ├── PHChangeObserver.h │ │ ├── PHChangeObserver.m │ │ ├── PHCollectionService.h │ │ ├── PHCollectionService.m │ │ ├── PHCreateMediaQueue.h │ │ ├── PHCreateMediaQueue.m │ │ ├── PHFetchOptionsService.h │ │ ├── PHFetchOptionsService.m │ │ ├── PHOperationResult.h │ │ ├── PHOperationResult.m │ │ ├── PHSaveAsset.h │ │ ├── PHSaveAsset.m │ │ ├── PHSaveAssetFileRequest.h │ │ ├── PHSaveAssetFileRequest.m │ │ ├── PHSaveAssetRequest.h │ │ ├── PHSaveAssetRequest.m │ │ ├── PHSaveAssetToFileOperationResult.h │ │ ├── PHSaveAssetToFileOperationResult.m │ │ ├── PHVideoExporter.h │ │ ├── PHVideoExporter.m │ │ ├── RCTConvert+RNPhotosFramework.h │ │ ├── RCTConvert+RNPhotosFramework.m │ │ ├── RCTImageResizer.h │ │ ├── RCTImageResizer.m │ │ ├── RNPFFileDownloader.h │ │ ├── RNPFFileDownloader.m │ │ ├── RNPFGlobals.h │ │ ├── RNPFHelpers.h │ │ ├── RNPFHelpers.m │ │ ├── RNPFImageLoader.h │ │ ├── RNPFImageLoader.m │ │ ├── RNPFManager.h │ │ ├── RNPFManager.m │ │ ├── RNPFUrlRequestHandler.h │ │ ├── RNPFUrlRequestHandler.m │ │ ├── RNPhotosFramework.h │ │ ├── RNPhotosFramework.m │ │ ├── SDAVAssetExportSession.h │ │ ├── SDAVAssetExportSession.m │ │ ├── iDebounce.h │ │ └── iDebounce.m │ ├── RNPhotosFramework.xcodeproj/ │ │ └── project.pbxproj │ └── RNPhotosFrameworkTests/ │ ├── Info.plist │ ├── PHAssetsService_getAssetsForFetchResultTests.m │ └── RCTConvert+RNPhotosFrameworkTests.m ├── local-cli/ │ ├── android/ │ │ └── android.js │ ├── bundle/ │ │ ├── assetPathUtils.js │ │ ├── buildBundle.js │ │ ├── bundle.js │ │ ├── bundleCommandLineArgs.js │ │ ├── getAssetDestPathAndroid.js │ │ ├── getAssetDestPathIOS.js │ │ ├── output/ │ │ │ ├── bundle.js │ │ │ ├── meta.js │ │ │ ├── prepack.js │ │ │ ├── unbundle/ │ │ │ │ ├── as-assets.js │ │ │ │ ├── as-indexed-file.js │ │ │ │ ├── build-unbundle-sourcemap-with-metadata.js │ │ │ │ ├── index.js │ │ │ │ ├── magic-number.js │ │ │ │ ├── util.js │ │ │ │ └── write-sourcemap.js │ │ │ └── writeFile.js │ │ ├── saveAssets.js │ │ ├── sign.js │ │ ├── signedsource.js │ │ └── unbundle.js │ ├── cli.js │ ├── cliEntry.js │ ├── commands.js │ ├── core/ │ │ ├── config/ │ │ │ ├── android/ │ │ │ │ ├── findAndroidAppFolder.js │ │ │ │ ├── findManifest.js │ │ │ │ ├── findPackageClassName.js │ │ │ │ ├── index.js │ │ │ │ └── readManifest.js │ │ │ ├── findAssets.js │ │ │ ├── index.js │ │ │ ├── ios/ │ │ │ │ ├── findProject.js │ │ │ │ └── index.js │ │ │ └── wrapCommands.js │ │ ├── findPlugins.js │ │ ├── getCommands.js │ │ └── makeCommand.js │ ├── default.config.js │ ├── dependencies/ │ │ └── dependencies.js │ ├── generate/ │ │ └── generate.js │ ├── generate-android.js │ ├── generator/ │ │ └── index.js │ ├── generator-android/ │ │ ├── index.js │ │ └── templates/ │ │ ├── bin/ │ │ │ ├── gradle/ │ │ │ │ └── wrapper/ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ └── gradlew.bat │ │ ├── package/ │ │ │ ├── MainActivity.java │ │ │ └── MainApplication.java │ │ └── src/ │ │ ├── app/ │ │ │ ├── BUCK │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.pro │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── res/ │ │ │ └── values/ │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── build.gradle │ │ ├── gradle.properties │ │ ├── keystores/ │ │ │ ├── BUCK │ │ │ └── debug.keystore.properties │ │ └── settings.gradle │ ├── generator-ios/ │ │ ├── index.js │ │ └── templates/ │ │ ├── app/ │ │ │ ├── AppDelegate.h │ │ │ ├── AppDelegate.m │ │ │ ├── Base.lproj/ │ │ │ │ └── LaunchScreen.xib │ │ │ ├── Images.xcassets/ │ │ │ │ └── AppIcon.appiconset/ │ │ │ │ └── Contents.json │ │ │ ├── Info.plist │ │ │ └── main.m │ │ ├── tests/ │ │ │ ├── Info.plist │ │ │ └── Tests.m │ │ └── xcodeproj/ │ │ ├── project.pbxproj │ │ └── xcshareddata/ │ │ └── xcschemes/ │ │ └── _xcscheme │ ├── generator-utils.js │ ├── init/ │ │ └── init.js │ ├── install/ │ │ ├── install.js │ │ └── uninstall.js │ ├── library/ │ │ └── library.js │ ├── link/ │ │ ├── android/ │ │ │ ├── copyAssets.js │ │ │ ├── fs.js │ │ │ ├── isInstalled.js │ │ │ ├── patches/ │ │ │ │ ├── applyParams.js │ │ │ │ ├── applyPatch.js │ │ │ │ ├── makeBuildPatch.js │ │ │ │ ├── makeImportPatch.js │ │ │ │ ├── makePackagePatch.js │ │ │ │ ├── makeSettingsPatch.js │ │ │ │ ├── makeStringsPatch.js │ │ │ │ └── revokePatch.js │ │ │ ├── registerNativeModule.js │ │ │ ├── unlinkAssets.js │ │ │ └── unregisterNativeModule.js │ │ ├── commandStub.js │ │ ├── getDependencyConfig.js │ │ ├── getProjectDependencies.js │ │ ├── groupFilesByType.js │ │ ├── ios/ │ │ │ ├── addFileToProject.js │ │ │ ├── addProjectToLibraries.js │ │ │ ├── addSharedLibraries.js │ │ │ ├── addToHeaderSearchPaths.js │ │ │ ├── copyAssets.js │ │ │ ├── createGroup.js │ │ │ ├── createGroupWithMessage.js │ │ │ ├── getBuildProperty.js │ │ │ ├── getGroup.js │ │ │ ├── getHeaderSearchPath.js │ │ │ ├── getHeadersInFolder.js │ │ │ ├── getPlist.js │ │ │ ├── getPlistPath.js │ │ │ ├── getProducts.js │ │ │ ├── hasLibraryImported.js │ │ │ ├── isInstalled.js │ │ │ ├── mapHeaderSearchPaths.js │ │ │ ├── registerNativeModule.js │ │ │ ├── removeFromHeaderSearchPaths.js │ │ │ ├── removeFromPbxItemContainerProxySection.js │ │ │ ├── removeFromPbxReferenceProxySection.js │ │ │ ├── removeFromProjectReferences.js │ │ │ ├── removeFromStaticLibraries.js │ │ │ ├── removeProductGroup.js │ │ │ ├── removeProjectFromLibraries.js │ │ │ ├── removeProjectFromProject.js │ │ │ ├── removeSharedLibraries.js │ │ │ ├── unlinkAssets.js │ │ │ └── unregisterNativeModule.js │ │ ├── link.js │ │ ├── pollParams.js │ │ ├── promiseWaterfall.js │ │ ├── promisify.js │ │ └── unlink.js │ ├── logAndroid/ │ │ └── logAndroid.js │ ├── logIOS/ │ │ └── logIOS.js │ ├── runAndroid/ │ │ ├── adb.js │ │ └── runAndroid.js │ ├── runIOS/ │ │ ├── findMatchingSimulator.js │ │ ├── findXcodeProject.js │ │ ├── parseIOSDevicesList.js │ │ └── runIOS.js │ ├── server/ │ │ ├── checkNodeVersion.js │ │ ├── findSymlinksPaths.js │ │ ├── formatBanner.js │ │ ├── middleware/ │ │ │ ├── copyToClipBoardMiddleware.js │ │ │ ├── cpuProfilerMiddleware.js │ │ │ ├── getDevToolsMiddleware.js │ │ │ ├── heapCapture/ │ │ │ │ ├── .npmignore │ │ │ │ ├── Makefile │ │ │ │ ├── heapCapture.html │ │ │ │ ├── out/ │ │ │ │ │ ├── aggrow.js │ │ │ │ │ ├── heapCapture.js │ │ │ │ │ └── table.js │ │ │ │ └── src/ │ │ │ │ ├── aggrow.js │ │ │ │ ├── heapCapture.js │ │ │ │ └── table.js │ │ │ ├── heapCaptureMiddleware.js │ │ │ ├── index.html │ │ │ ├── indexPage.js │ │ │ ├── jscProfilerMiddleware.js │ │ │ ├── loadRawBodyMiddleware.js │ │ │ ├── openStackFrameInEditorMiddleware.js │ │ │ ├── statusPageMiddleware.js │ │ │ └── systraceProfileMiddleware.js │ │ ├── runServer.js │ │ ├── server.js │ │ └── util/ │ │ ├── attachHMRServer.js │ │ ├── copyToClipBoard.js │ │ ├── debugger.html │ │ ├── debuggerWorker.js │ │ ├── launchEditor.js │ │ ├── messageSocket.js │ │ └── webSocketProxy.js │ ├── setup_env.bat │ ├── setup_env.sh │ ├── upgrade/ │ │ └── upgrade.js │ ├── util/ │ │ ├── Config.js │ │ ├── assertRequiredOptions.js │ │ ├── copyAndReplace.js │ │ ├── isPackagerRunning.js │ │ ├── isValidPackageName.js │ │ ├── log.js │ │ ├── parseCommandLine.js │ │ └── walk.js │ └── wrong-react-native.js ├── package.json ├── react-native-photos-framework.podspec ├── scripts/ │ ├── objc-test-ios.sh │ └── objc-test.sh └── src/ ├── .watchmanconfig ├── ajax-helper.js ├── album-query-result-base.js ├── album-query-result-collection.js ├── album-query-result.js ├── album.js ├── asset.js ├── change-observer-handler.js ├── image-asset.js ├── index.js ├── instagram-app-sort.js ├── photo-app-sort.js ├── uuid-generator.js ├── video-asset.js └── video-props-resolver.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # 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 # Android/IJ # .idea .gradle local.properties # node.js # node_modules/ npm-debug.log article-web/article-web.zip example/react-native-photos-framework/src/ # REMINDER: do not edit Icon^M^M Carriage Returns Icon # END REMINDER ================================================ FILE: .travis.yml ================================================ language: objective-c osx_image: xcode8.2 install: - nvm use 6.9.1 - wget https://github.com/yarnpkg/yarn/releases/download/v0.18.1/yarn-0.18.1.js - export yarn="node $(pwd)/yarn-0.18.1.js" - $yarn install - cd example && $yarn install && cd .. script: - if [[ "$TEST_TYPE" = objc-ios ]]; then set -o pipefail && xcodebuild test -verbose -project example/ios/Example.xcodeproj -scheme Example -destination 'id=E40727B3-41FB-4D6E-B4CB-BFA87109EB12' | xcpretty; fi - if [[ "$TEST_TYPE" = js ]]; then npm test -- --maxWorkers=1; fi env: matrix: - TEST_TYPE=objc-ios - TEST_TYPE=js branches: only: - master - /^.*-stable$/ notifications: email: recipients: - olof.dahlbom@me.com on_failure: change on_success: change ================================================ FILE: .vscode/launch.json ================================================ { "version": "0.2.0", "configurations": [ { "name": "Debug Android", "program": "${workspaceRoot}/.vscode/launchReactNative.js", "type": "reactnative", "request": "launch", "platform": "android", "internalDebuggerPort": 9090, "sourceMaps": true, "outDir": "${workspaceRoot}/.vscode/.react" }, { "name": "Debug iOS", "program": "${workspaceRoot}/.vscode/launchReactNative.js", "type": "reactnative", "request": "launch", "platform": "ios", "target": "iPhone 5s", "internalDebuggerPort": 9090, "sourceMaps": true, "outDir": "${workspaceRoot}/.vscode/.react" } ] } ================================================ FILE: .watchmanconfig ================================================ { "ignore_dirs": [ ".git", "node_modules" ] } 123 ffmpeg -i ~/Desktop/Untitled.mov -s 304x540 -pix_fmt rgb24 -r 15 -f gif - | gifsicle --optimize=4 --delay=4 > ~/Desktop/out.gif cp ~/Desktop/out.gif ~/Dropbox/Public/screenshots/Screencast-`date +"%Y.%m.%d-%H.%M"`.gif ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2016 Juli Racca Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ test: xcodebuild test -workspace ios/RNPhotosFramework.xcworkspace -scheme RNPhotosFramework -destination 'platform=iOS Simulator,name=iPhone 5s' ================================================ FILE: README.md ================================================ # react-native-photos-framework [![Build Status](https://travis-ci.org/olofd/react-native-photos-framework.svg?branch=master)](https://travis-ci.org/olofd/react-native-photos-framework) [![npm version](https://badge.fury.io/js/react-native-photos-framework.svg)](https://badge.fury.io/js/react-native-photos-framework) [![Beerpay](https://beerpay.io/olofd/react-native-photos-framework/badge.svg?style=beer)](https://beerpay.io/olofd/react-native-photos-framework) ### Example project #### NOTE: This is not a GUI-component, it's an API. The example project just shows off some of the the capabilities of this API. ![](https://media.giphy.com/media/3o6Ztqdc8OF3FAgAiQ/source.gif) #### Breaking Changes react-native header imports have changed in v0.40, and that means breaking changes for all! [Reference PR & Discussion](https://github.com/lwansbrough/react-native-camera/pull/544). This library is updated to work with the new imports. Use version < 0.0.64 if your still < RN 0.40. ### Description Load photos/videos and more from CameraRoll and iCloud. Uses Apples photos framework. - Advanced options for loading and filtering. - Support for displaying both Images and Videos simply in your app. - Create, modify, delete photos, videos and albums. - Support for sending photos and videos to a server using Ajax. - Change-tracking. (eg. someone takes a new photo while your app is open, this library will provide you with events to refresh your collection so it will display the latest changes to the photo-library). React Native comes with it's own CameraRoll library. This however uses ALAssetLibrary which is a deprecated API from Apple and can only load photos and videos stored on the users device. This is not what your user expects today. Most users photos live on iCloud and these won't show if you use ALAssetLibrary. If you use this library (Photos framework) you can display the users local resources and the users iCloud resources. ### Installation: `npm i react-native-photos-framework --save && react-native link react-native-photos-framework` NOTE: When running `npm install` this library will try to automatically add `NSPhotoLibraryUsageDescription` to your Info.plist. Check that it is there after the install or update it's value from the default: `Using photo library to select pictures` (Will not do anything if you have already defined this key in Info.plist) # Simple example: ~~~js /** * Sample React Native App * https://github.com/facebook/react-native * @flow */ import React, {Component} from 'react'; import {AppRegistry, StyleSheet, Text, View, Image} from 'react-native'; import RNPhotosFramework from 'react-native-photos-framework'; export default class AwesomeProject extends Component { constructor() { super(); this.state = { images : [] }; } componentDidMount() { RNPhotosFramework.requestAuthorization().then((statusObj) => { if (statusObj.isAuthorized) { RNPhotosFramework.getAlbums({ type: 'smartAlbum', subType: 'smartAlbumUserLibrary', assetCount: 'exact', fetchOptions: { sortDescriptors: [ { key: 'title', ascending: true } ], includeHiddenAssets: false, includeAllBurstAssets: false }, //When you say 'trackInsertsAndDeletes or trackChanges' for an albums query result, //They will be cached and tracking will start. //Call queryResult.stopTracking() to stop this. ex. on componentDidUnmount trackInsertsAndDeletes: true, trackChanges: false }).then((queryResult) => { const album = queryResult.albums[0]; return album.getAssets({ //The fetch-options from the outer query will apply here, if we get startIndex: 0, endIndex: 10, //When you say 'trackInsertsAndDeletes or trackAssetsChange' for an albums assets, //They will be cached and tracking will start. //Call album.stopTracking() to stop this. ex. on componentDidUnmount trackInsertsAndDeletes: true, trackChanges: false }).then((response) => { this.setState({ images : response.assets }); }); }); } }); } renderImage(asset, index) { return ( ); } render() { return ( {this.state.images.map(this.renderImage.bind(this))} ); } } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#F5FCFF' }, welcome: { fontSize: 20, textAlign: 'center', margin: 10 }, instructions: { textAlign: 'center', color: '#333333', marginBottom: 5 } }); AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject); ~~~ # API-documentation: ## Library (Top level): ## Static methods: ### authorizationStatus ~~~js RNPhotosFramework.authorizationStatus().then(() => { }); ~~~ Signature: `RNPhotosFramework.authorizationStatus() : Promise<{status : string, isAuthorized : boolean}>`. Fetches the current authorization-status. NOTE: You can receive the following statuses : * `notDetermined` //Before user has granted permission, * `restricted` //User is restricted by policy, cannot use Photos, * `denied` //User has denied permission, * `authorized` //User has granted permission ### requestAuthorization ~~~js RNPhotosFramework.requestAuthorization().then((statusObj) => { if(statusObj.isAuthorized) { ...start using the library. } }); ~~~ Signature: `RNPhotosFramework.requestAuthorization() : Promise<{status : string, isAuthorized : boolean}>`. This will prompt the user to grant access to the user library at first start. If you do not call this method explicitly before using any of the other functions in this library, the grant-access-dialog will appear for the user automatically at the first function-call into the library. But only one function-call can automatically trigger this dialog, so if another call comes into Photos Framework before the user has granted you access, that function-call will fail. Therefore I urge you to call this method explicitly before you start using the rest of the library to not experience unexpected behaviour. NOTE: You do not have to first check the authorizationStatus before calling this. If the user has granted access before, this will just return authorized-status. NOTE: See available statuses in doc. about: `authorizationStatus` ## Working with Content: ##### `fetchOptions` fetchOptions is a query-object which can be sent both when fetching albums with `getAlbums` and when fetching assets with `getAssets`. Below you can see the available options for fetchOptions. You can also read Apple's documentation around [PHFetchOptions here](https://developer.apple.com/reference/photos/phfetchoptions). (Many of the args map one-to-one with native data structures.) | Prop | Default | Type | Description | | :------------ |:---------------:| :---------------:| :-----| | mediaTypes (Only for `getAssets`) | - | `array` | Defines what mediaType the asset should be. Array combined with OR-operator. e.g. ['image', 'video'] will return both photos and videos. Converted in Native to PHAssetMediaType. Accepted values: `image`, `video`, `audio`, `unknown` | | mediaSubTypes (Only for `getAssets`) | - | `array` | Defines what subtype the asset should be. Array combined with OR-operator. e.g. ['photoPanorama', 'photoHDR'] will return both panorama and HDR-assets. Converted in Native to PHAssetMediaSubtype. Accepted enum-values: `none`, `photoPanorama`, `photoHDR`, `photoScreenshot`, `photoLive`, `videoStreamed`, `videoHighFrameRate`, `videoTimeLapse` (mediaTypes and mediaSubTypes are combined with AND-operator) | | sourceTypes (Only for `getAssets`) | - | `array` | Defines where the asset should come from originally. Array combined with OR-operator. Converted in Native to PHAssetSourceType. Accepted enum-values: `none`, `userLibrary`, `cloudShared`, `itunesSynced`. (not supported and ignored in iOS 8) | | includeHiddenAssets | false | `boolean` | A Boolean value that determines whether the fetch result includes assets marked as hidden. | | includeAllBurstAssets | false | `boolean` | A Boolean value that determines whether the fetch result includes all assets from burst photo sequences. | | fetchLimit | 0 | `number` | The maximum number of objects to include in the fetch result. Remember to not use this in the wrong way combined with startIndex and endIndex. 0 means unlimited. | | sortDescriptors | - | `array<{key : , ascending : }>` | Multiple sortDescriptors which decide how the result should be sorted. | # Retrieving Assets (photos/videos/audio): ~~~js import RNPhotosFramework from 'react-native-photos-framework'; RNPhotosFramework.getAssets({ //Example props below. Many optional. // You can call this function multiple times providing startIndex and endIndex as // pagination. startIndex: 0, endIndex: 100, fetchOptions : { // Media types you wish to display. See table below for possible options. Where // is the image located? See table below for possible options. sourceTypes: ['userLibrary'], sortDescriptors : [ { key: 'creationDate', ascending: true, } ] } }).then((response) => console.log(response.assets)); ~~~ ###### Props to `getAssets` | Prop | Default | Type | Description | | :------------ |:---------------:| :---------------:| :-----| | fetchOptions | - | `object` | See above. | | startIndex | 0 | `number` | startIndex-offset for fetching | | endIndex | 0 | `number` | endIndex-offset stop for fetching | | includeMetadata | false | `boolean` | Include a lot of metadata about the asset (See below). You can also choose to get this metaData at a later point by calling `asset.getMetadata()` (See below) | | includeResourcesMetadata | false | `boolean` | Include metadata about the orginal resources that make up the asset. Like type and original filename. You can also choose to get this metaData at a later point by calling `asset.getResourcesMetadata()`. | | prepareForSizeDisplay | - | `Rect(width, height)` | The size of the image you soon will display after running the query. This is highly optional and only there for optimizations of big lists. Prepares the images for display in Photos by using PHCachingImageManager | | prepareScale | 2.0 | `number` | The scale to prepare the image in. | | assetDisplayStartToEnd | false | `boolean` | Retrieves assets from the beginning of the library when set to true. Using this sorting option preserves the native order of assets as they are viewed in the Photos app. | | assetDisplayBottomUp | false | `boolean` | Used to arrange assets from the bottom to top of screen when scrolling up to view paginated results. | ### Example of asset response with `includeMetadata : true` ~~~ creationDate : 1466766146 duration : 17.647 (video) width : 1920 height : 1080 isFavorite : false isHidden : false localIdentifier : "3D5E6260-2B63-472E-A38A-3B543E936E8C/L0/001" location : Object mediaSubTypes : null mediaType : "video" modificationDate : 1466766146 sourceType : "userLibrary" uri : "photos://3D5E6260-2B63-472E-A38A-3B543E936E8C/L0/001" ~~~ (`sourceType` is not supported in iOS 8) # Retrieving albums and enumerating their assets: ~~~js RNPhotosFramework.getAlbums({ type: 'album', subType: 'any', assetCount: 'exact', fetchOptions: { sortDescriptors : [ { key: 'title', ascending: true } ], includeHiddenAssets: false, includeAllBurstAssets: false }, //When you say 'trackInsertsAndDeletes or trackChanges' for an albums query result, //They will be cached and tracking will start. //Call queryResult.stopTracking() to stop this. ex. on componentDidUnmount trackInsertsAndDeletes : true, trackChanges : false }).then((queryResult) => { const album = queryResult.albums[0]; return album.getAssets({ //The fetch-options from the outer query will apply here, if we get startIndex: 0, endIndex: 10, //When you say 'trackInsertsAndDeletes or trackAssetsChange' for an albums assets, //They will be cached and tracking will start. //Call album.stopTracking() to stop this. ex. on componentDidUnmount trackInsertsAndDeletes : true, trackChanges : false }).then((response) => { console.log(response.assets, 'The assets in the first album'); }); }); ~~~ ###### Props to `getAlbums` Get albums allow to query the Photos Framework for asset-albums. Both User-created ones and Smart-albums. Note that Apple creates a lot of dynamic, so called Smart Albums, like : 'Recently added', 'Favourites' etc. NOTE: There is also another method called `getAlbumsMany`. This could be considered a low-level-method of the API. It is constructed so that this library can build more accessible methods on top of one joint native-call: like getUserTopAlbums in pure JS. The getAlbumsMany-api can take multiple queries (array) and return an array. NOTE about Apple Bug for album titles in iOS 10. The album.title property is suppose to be localized to your language out of the box. But this will only work if you have a language translation file included in your project. The native property is called localizedTitle. If you only see english names as titles for smart albums, eg. 'All Photos' even though you run on a different language iPhone, check this question out: http://stackoverflow.com/questions/42579544/ios-phassetcollection-localizedtitle-always-returning-english-name | Prop | Default | Type | Description | | :------------ |:---------------:| :---------------:| :-----| | fetchOptions | - | `object` | See above. | | assetFetchOptions | - | `object` | Fetch options used when loading assets from album returned. You can choose to pass these fetchOptions here to affect `previewAssets` and `assetCount` in the album according to these options. Note: If you supply fetchOptions when later calling getAssets, those options will override these options. | | type | `album` | `string` | Defines what type of album/collection you wish to retrieve. Converted in Native to PHAssetCollectionType. Accepted enum-values: `album`, `smartAlbum`, `moment` | | subType | `any` | `string` | Defines what subType the album/collection you wish to retrieve should have. Converted in Native to PHAssetCollectionSubtype. Accepted enum-values: `any`, `albumRegular`, `syncedEvent`, `syncedFaces`, `syncedAlbum`, `imported`, `albumMyPhotoStream`, `albumCloudShared`, `smartAlbumGeneric`, `smartAlbumPanoramas`, `smartAlbumVideos`, `smartAlbumFavorites`, `smartAlbumTimelapses`, `smartAlbumAllHidden`, `smartAlbumRecentlyAdded`, `smartAlbumBursts`, `smartAlbumSlomoVideos`, `smartAlbumUserLibrary`, `smartAlbumSelfPortraits`, `smartAlbumScreenshots` | | assetCount | `estimated` | `string/enum` | You can choose to get `estimated` count of the collection or `exact`-count. Of course these have different performance-impacts. Returns -1 if the estimated count can't be fetched quickly. Remember that your of course fetchOptions affects this count. | | previewAssets | - | `number` | If you set this to a number, say 2, you will get the first two images from the album included in the album-response. This is so you can show a small preview-thumbnail for the album if you like to. | | includeMetadata | false | `boolean` | Include some meta data about the album. You can also choose to get this metaData at a later point by calling album.getMetadata (See below) | | noCache | `false` | `boolean` | If you set this flag to true. The result won't get cached or tracked for changes. | | preCacheAssets | `false` | `boolean` | If you set this property to true all assets of all albums your query returned will be cached and change-tracking will start. | | trackInsertsAndDeletes | `false` | `boolean` | If you set this to true. You will get called back on `queryResult.onChange` when a Insert or Delete happens. See observing changes below for more details. | | trackChanges | `false` | `boolean` | If you set this to true. You will get called back on `queryResult.onChange` when a Change happens to the query-result. See observing changes below for more details. | # Working with Albums: ## Static methods: ### Base methods: ~~~js //Fetches albums for params. See above RNPhotosFramework.getAlbums(params) //Fetches many queries //as SingleQueryResult : boolean. if true, will return response as one single response. RNPhotosFramework.getAlbumsMany([params, params...], asSingleQueryResult); //Prebuilt query for fetching the most typical albums: //Camera-Roll, User-albums and user-shared-albums. RNPhotosFramework.getAlbumsCommon(params) ~~~ ### createAlbum ~~~js RNPhotosFramework.createAlbum('test-album').then((album) => { //You can now use the album like any other album: return album.getAssets().then((photos) => {}); }); ~~~ Signature: RNPhotosFramework.createAlbum(albumTitle) : Promise. There is also another multi-method you can use here: Signature: RNPhotosFramework.createAlbums(albumTitles) : Promise. NOTE: Alums can have the same name. All resources in Photos are unique on their localIdentifier. You can use the below methods to tackle this: ### getAlbumsByTitle ~~~js RNPhotosFramework.getAlbumsByTitle('test-album').then((albums) => {}); ~~~ Signature: RNPhotosFramework.getAlbumsByTitle(albumTitle) : Promise>. Many albums can have the same title. Returns all matching albums. There is also another multi-method you can use here: Signature: RNPhotosFramework.getAlbumsByTitles(albumTitles) : Promise>. Signature: RNPhotosFramework.getAlbumsWithParams({albumTitles, ...otherThingLikeFetchOptionsOrType/SubType}) : Promise>. ### getAlbumByLocalIdentifier and getAlbumByLocalIdentifiers ~~~js RNPhotosFramework.getAlbumByLocalIdentifier(localIdentifier).then((album) => {}); ~~~ Signature: RNPhotosFramework.getAlbumByLocalIdentifier(localIdentifier) : Promise. All alums carry their localIdentifier on album.localIdentifier. ## Album instance-methods: ### addAssetToAlbum and addAssetsToAlbum ~~~js album.addAssetToAlbum(asset).then((status) => {}); ~~~ Signature: album.addAssetToAlbum(asset) : Promise. Add an asset/assets to an album. NOTE: Can only be called with assets that are stored in the library already. If you have a image that you want to save to the library see createAsset. ### removeAssetFromAlbum and removeAssetsFromAlbum ~~~js album.removeAssetFromAlbum(asset).then((status) => {}); ~~~ Signature: album.removeAssetFromAlbum(asset) : Promise. Remove asset from album. NOTE: Can only be called with assets that are stored in the library already. If you have a image that you want to save to the library see createAsset. ### updateTitle ~~~js album.updateTitle(newTitle).then((status) => {}); ~~~ Signature: album.updateTitle(string) : Promise. Change title on an album. ### delete ~~~js album.delete().then((status) => {}); ~~~ Signature: album.delete() : Promise. Delete an album. ### getMetadata ~~~js album.getMetadata().then((mutatedAlbumWithMetadata) => {}); ~~~ Fetch meta data for a specific album. You can also include metadata on all albums in the first `getAlbum`-call by explicitly setting option `includeMetadata: true`. # Working with Assets (Images/Photos): When you retrieve assets from the API you will get back an Asset object. There is nothing special about this object. I've defined it as a class so that it can have some instance-methods. ## All Assets. (Video/Image) These are methods and options that apply to all kinds of assets. (NOTE: Creating new assets has it's own chapter down below). ## Asset instance-methods: #### setHidden ~~~js asset.setHidden(hiddenBoolean).then((resultOfOperation) => { }); ~~~ Hides or un-hides a specific asset. Will prompt the user when an asset is about to be hidden. #### setFavorite ~~~js asset.setFavorite(favoriteBoolean).then((resultOfOperation) => { }); ~~~ Marks/Unmarks the asset as favorite. #### setCreationDate ~~~js asset.setCreationDate(jsDate).then((resultOfOperation) => { }); ~~~ Updates the assets creationDate. #### setLocation ~~~js asset.setLocation({ lat : Number //required, lng : Number //required, altitude : Number //optional, Altitue above sea-level heading : Number //optional, cource/heading from 0 (North) to 359.9. speed : Number //optional, speed in m/s. timeStamp : JSDate //optional, timestamp. Defaults to Now. }).then((resultOfOperation) => { }); ~~~ Updates the assets location. #### getMetadata ~~~js asset.getMetadata().then((mutatedAssetWithMetadata) => {}); ~~~ Fetch metadata for a specific asset. You can also include metadata on all assets in the first `getAsset`-call by explicitly setting option `includeMetadata: true`. #### getResourcesMetadata ~~~js asset.getResourcesMetadata().then((mutatedAssetWithResourcesMetadata) => { console.log(mutatedAssetWithResourcesMetadata.resourcesMetadata); }); ~~~ Fetch resource-metadata for a specific asset, this includes original filename, type, uti (uniformTypeIdentifier) and localidentifier. You can also include resource-metadata on all assets in the first `getAsset`-call by explicitly setting option `includeResourcesMetadata: true`. ### Update Asset metaData NOTE: When updating metaData, there is two ways of dealing with the result of the update operation. Either your asset-collection updates automatically by using `Change-Tracking` (See below). This means that the updated asset will be replaced in your collection, but the asset you executed the change on will be unaffected. So calling `setHidden(true)` will still have isHidden : false, after your update, but it will be replaced with a new asset in your collection via `Change-Tracking`. (Remember to track changes with `trackChanges : true` when calling `getAssets`) If you choose to NOT use `Change-Tracking` you can call `refreshMetadata` on the asset after your update-operation: ~~~js asset.setHidden(hiddenBoolean).then((resultOfOperation) => { asset.refreshMetadata().then(() => { console.log('The JS-asset should now reflect your changes'); }); }); ~~~ NOTE2: You can update multiple assets at once by calling ~~~js RNPhotosFramework.updateAssets({ [assetOne.localIdentifier] : { //Will only update properties provided: hidden, favorite, creationDate, location }, [assetTwo.localIdentifier] : { hidden, favorite, creationDate, location } ...etc }); ~~~ #### delete ~~~js asset.delete().then((status) => { }); ~~~ Delete asset. ## Images/Photo-Assets An Image/Photo-asset is fully compatible with RN's ``-tag. This includes all resizeModes. NOTE: Use the property `.image` on an asset for the ``-tag. Otherwise RN will freeze your asset object. And they are, right now at least, mutable. ## Image-Asset instance-methods: #### getImageMetadata ~~~js asset.getImageMetadata().then((mutatedAssetWithImageMetadata) => { console.log(mutatedAssetWithResourcesMetadata.imageMetadata); }); ~~~ Fetch image specific metadata for a specific image-asset, this includes formats and sizes. ## withOptions for Images/Photos `withOptions` define special rules and options when loading an asset. If you want to know more about how an asset is loaded. Read below on chapter `About Asset-Loaders`. ### deliveryMode Apple's Photo Framework will download images from iCloud on demand, and will generally be very smart about caching and loading resources quickly. You can however define how an Image should be loaded. We have 3 different options in PHImageRequestOptionsDeliveryMode: ~~~ PHImageRequestOptionsDeliveryModeOpportunistic = 0, // client may get several image results when the call is asynchronous or will get one result when the call is synchronous PHImageRequestOptionsDeliveryModeHighQualityFormat = 1, // client will get one result only and it will be as asked or better than asked (sync requests are automatically processed this way regardless of the specified mode) PHImageRequestOptionsDeliveryModeFastFormat = 2 // client will get one result only and it may be degraded ~~~ This library defaults to loading assets with `PHImageRequestOptionsDeliveryModeHighQualityFormat`. This can be considered to be the same as RN normally loads images. It will simply download the image in the size of your (iCloud-images are stored in multiple sizes, and Photos Framework will download the one closest to your target size) and display it. But you can choose to use the other two deliveryMode's to. you do this by calling: ~~~js const assetWithNewDeliveryMode = asset.withOptions({ //one of opportunistic|highQuality|fast deliveryMode : 'opportunistic' }); ~~~ If you choose to use opportunistic here you will see a low-res-version of the image displayed while the highQuality version of the resource is downloaded. NOTE: This library will call correct lifecycle callback's on your image-tag when this is used: the `` ## Video-Assets Video assets can be played by using a special branch of the great library `react-native-video`. This branch adds the capability of loading Videos from Photos-framework and works as expected, but is otherwise the same as the orignal project. So you can play local or remote files as well and if you already use `react-native-video` for other content, you should just be able to replace the version. NOTE: Let me and the `react-native-video`-guys know if you want this to go into thir master. ### Install `react-native-video`: `npm install git://github.com/olofd/react-native-video.git#react-native-photos-framework --save && react-native link` or add : `"react-native-video": "git://github.com/olofd/react-native-video.git#react-native-photos-framework"` to your package.json and run npm install. ### Displaying video ~~~jsx