Mastering the Camera Roll in React Native

Accessing, sharing, and saving images to the device Camera Roll using React Native.

In this post, I’ll walk through how to:

  1. View images in the device camera roll
  2. Download image paths to the device camera roll
  3. Share images from the device camera roll

The final code for this project can be found here.

What we will be building.

One of the advantages of React Native is the ease of access the framework gives developers to the actual device APIs. One of those that is especially impressive is the native camera roll.

The app will use the amazing Unsplash image API to fetch images for the application.

Below is a quick look at what we will be building.

Getting Started

The first thing we need to do is create a new React Native project and install our dependencies.

In a working directory, create a new project using react-native init (I will be calling my project rncameraroll, but feel free to call it whatever you would like).

react-native init rncameraroll

Now that the project is created, enter the new directory.

Our project will be using a few dependencies:

  1. react-navigation for navigation
  2. react-native-sharefor sharing to twitter / facebook / etc
  3. react-native-fetch-blobfor file system access

Let’s install these dependecies using either yarn or npm (I recommend yarn as it’s much faster)

yarn add react-navigation react-native-share react-native-fetch-blob
If you have issues getting the project to compile after implementing React Navigation, you may need to update the version in the package.json to "git+https://github.com/react-community/react-navigation.git#7edd9a7" (then reinstall) until they fix the error in the library (see discussion here).

Then we need to link the libraries to make sure the native dependencies are integrated with the project. In the terminal, type the following:

react-native link

Next, to access the CameraRoll in iOS, we need to add the CameraRoll Xcode project to our React Native Project.

If you already know how this is done, you can skip to the next section. If not, check out the video below or take a look at the manual linking section of the documentation.

Adding Camera Roll to an iOS Project in React Native

Creating Unsplash Client ID

To access the Unsplash API, a client ID is needed.

To get this, we must create an account at https://unsplash.com/developers

Then, click register as a developer and click on the New Application button.

The only permission you will need is Public Access.

Once you click save you should receive an Application ID. Save this id for later.

Building the App

Now that everything is installed and ready to go, we can go ahead into both index.ios.js and index.android.js and set up the navigation.

In index.ios.js / index.android.js, update with the following code:

  1. Import StackNavigator from react-navigation
  2. Import two components that we will create next: App and ImageBrowser
  3. Create an instance of StackNavigator, passing in our two routes, and save it in a variable called Navigation
  4. Register the application, passing in the Navigation component we just created

Now, we will go ahead and create the App component. This will be the main screen where we land when the application loads.

App.js

The entire code is below and we will go over it in just a moment, but there are two main methods to talk about concerning what is going on.

getPhotos()

getPhotos = () => {
CameraRoll.getPhotos({
first: 20,
assetType: 'All'
})
.then(r => this.setState({ photos: r.edges }))
}

getPhotos is the method we call to get photos from the camera roll. It takes an object with some configuration as parameters and returns a promise that contains an object with two keys: edges and page_info. edges contains the actual image paths so that is all we need for this example (r.edges).

When we get the photos back, we update our state with the images.

For this example, we only retrieve the first 20 images and we pass in All as the assetType. This means that we will not only get images back, but also Videos.

For all of the configuration for this method, check out the docs.

share()

share = () => {
const image = this.state.photos[this.state.index].node.image.uri
RNFetchBlob.fs.readFile(image, 'base64')
.then((data) => {
let shareOptions = {
title: "React Native Share Example",
message: "Check out this photo!",
url: `data:image/jpg;base64,${data}`,
subject: "Check out this photo!"
}
Share.open(shareOptions)
.then((res) => console.log('res:', res))
.catch(err => console.log('err', err))
})
}
share() brings up native sharing popup

Share basically takes a selected image, encodes it into base64, and then calls Share.open()allowing us to access the native share popup and passing in the selected image along with some other info, including a title, message, and subject (subject is for email only).

We are combining functionality from both react-native-fetch-blob and react-native-share.

Notice the url field. To understand more how this field works, check out the documentation for react-native-share. The url can either be a remote url, or a local file as long as it is configured in the correct way.

  1. Import all of the React Native and installed dependencies we will be needing
  2. Destructure width from Dimensions. We will use this to get a consistent width and height for our images
  3. static NavigationOptions allows us to set a page title. This is part of react-navigation
  4. In the initialState, modalVisible will show and hide the modal containing the CameraRoll images, photos will hold the actual images, and index will keep track of the selected index when we are choosing which image to share

We have gone over the other two main methods, and the rest should be self explanatory.

ImageBrowser.js

The entire code for this file is also below and we will go over it as well, but let’s first take a look at a couple of methods:

fetchPhotos

fetchPhotos = () => {
this.setState({ loading: true })
fetch(`https://api.unsplash.com/photos/?page=${this.state.page}&per_page=30&client_id=${YOURCLIENTIDFROMUNSPLASH}`)
.then(res => res.json())
.then(images => {
this.state.images.push(...images)
this.setState({
images: this.state.images,
loading: false,
page: this.state.page + 1
})
})
}

This is the main method that hits the Unsplash API and returns the photos for our app.

In this method, we automatically set loading to true, which will show a loading indicator until the api returns.

Notice YOURCLIENTIDFROMUNSPLASH. This is where you will place your client id you received after signing up to the Unsplash developer account.

We are using a couple of URL query strings in the request as well, notably:

page= and per_page=

We consistently set per_page to 30, but we increment the page after each request, so the next time we get new photos:

page: this.state.page + 1

Once we have the returned images, we add these to our state, stop the loading indicator, and increment the page as we discussed already.

saveToCameraRoll = (image) => {}

saveToCameraRoll = (image) => {
if (Platform.OS === 'android') {
RNFetchBlob
.config({
fileCache : true,
appendExt : 'jpg'
})
.fetch('GET', image.urls.small)
.then((res) => {
CameraRoll.saveToCameraRoll(res.path())
.then(Alert.alert('Success', 'Photo added to camera roll!'))
.catch(err => console.log('err:', err))
})
} else {
CameraRoll.saveToCameraRoll(image.urls.small)
.then(Alert.alert('Success', 'Photo added to camera roll!'))
}
}

In iOS, CameraRoll allows us to call CameraRoll.saveToCameraRoll on remote urls which is really nice and easy!

In Android, we actually need to download the file and then save it.

To do so on Android, we use RNFetchBlob.fetch() and use the returned file url to save to the camera roll.

  1. Import all of the React Native and installed dependencies we will be needing for this file.
  2. Destructure width and height from Dimensions. We will be using these dimensions for styling purposes.
  3. static navigationOptions allows us to set a page title. This is part of react-navigation
  4. Create an initial state. Loading is set to true, and then updated to false once them images are returned from the Unsplash API. Images will also be updated as soon as we receive images from the call to the Unsplash API. page is set to 1. We will be using this for paging / pagination when calling the Unsplash api by including it in the query
  5. In componentDidMount, we call the fetchPhotos method that we went over above

The rest of the code should be self explanatory as it is mainly JSX and styling.

That should be all you need to run the app!

The final code for this repo is located here.

My Name is Nader Dabit . I am a software consultant and trainer and the founder of React Native Training where we teach developers at top companies around the world how to quickly get up and running with React Native.
If you like React Native, checkout out our podcast — React Native Radio on Devchat.tv with Gant Laborde Kevin Old Ali Najafizadeh and Peter Piekarczyk
If you or your company are interested in on site training, check out ReactNativeTraining.com
Also, check out my book, React Native in Action now available from Manning Publications
If you enjoyed this article, please recommend and share it! Thanks for your time