Here at Finimize we’ve been working hard to bring you amazing audio to listen to in the app. In this post, we’re going to dive a bit into how we built our audio player, and how you can do the same thing in react-native.
Specifically, in order to create a fully-functional, global audio player, we harnessed the following features from a react-native audio library:
- Streaming audio
- Playing audio files over the network
- Adding support for playing audio in the background
- Seeking, skipping, adjusting rates, and jumping around
We also implemented specific features on top of the features provided by the library, including:
- An easy, globally accessible interface to create playlists inside our app
- Strong typing to help make sure that we’re passing in the correct object structure
We don’t cover it in this post, but with the features we build here, we can extend to persist the playlist state through app sessions, and even add offline audio support.
We’ll explore the engineering choices we made while implementing the audio functionality, as well as the code required to get you up and running with playing audio files, whether that’s music, podcasts, or your own personal radio show.
Throughout this post, our snippets will be in Typescript — if you’re not used to Typescript syntax, don’t fret! We’ll be here to walk you through the code.
The first step is to explore possible library options. When we implemented this, the current options we had were:
- react-native-sound (https://github.com/zmxv/react-native-sound)
- react-native-audio-toolkit (https://github.com/react-native-community/react-native-audio-toolkit)
- react-native-track-player (https://react-native-kit.github.io/react-native-track-player/documentation/)
Using the above diagram, we know that Finimize falls in the upper-right quadrant, as we wanted to ability to stream audio files (as they can be often quite large), over the network (so we don’t have to download all the files first). Neither react-native-sound or react-native-audio-toolkit support streaming audio. react-native-track-player supports background audio on iOS and Android, streaming audio, custom audio controls, as well as supporting passing in audio urls. For these reasons, we decided to use react-native-track-player. This allowed us to work towards building a cross-platform audio player.
A good breakdown of the various audio player libraries, and their strengths can be found here: https://medium.com/@emmettharper/the-state-of-audio-libraries-in-react-native-7e542f57b3b4 (Oct 2018).
As stated above, on top of the basic functionality provided by the player, we needed some extra functionality such as adding to playlists. We also wanted to store the playlist in memory, so we could use it offline, and persist the audio state through app uses. So we decided to wrap the library with a playlist controller that keeps us in full control of what’s going on. Let’s get started — full-steam ahead! 🚂
Using the example from the documentation (https://react-native-kit.github.io/react-native-track-player/)
First things first — we need to add, and set up the library. The documentation has a great page here (https://react-native-kit.github.io/react-native-track-player/install/) which can help you to get started.
Next, we wanted to set up the audio player. We decided to wrap the functionality of the library, so we can be in full control of the state of the player at any given time. Let’s create a file.
This now gives us easy access to the player, and we can set it up in our App component, either using ComponentDidMount, or UseEffect, depending on whether you’re using a functional or class component. We then need to register the playback service with our app — usually in our main index file, and initialise our track player component.
Again, the library has a good guide for getting started with using the player, found here: https://react-native-kit.github.io/react-native-track-player/api/
We’re now ready to use our player! Let’s flesh it out so we can add some more features. We now want to add the ability to add tracks to the track player, so let’s add the below function to the file.
We can now add tracks to the Track Player! We’d want to be able to do basic things to those tracks, so let’s add some basic utility helper functions to wrap expected functionality from the library: play / pause, next, and previous.
Great. We can now start working on our Playlist API, which will allow us to manage adding multiple items to the track player, and move through them. In general, we want our Playlist API to be our main point of contact with the track player — so we again wrap some of the functionality we made in the Track Player, so we don’t have to interact with it directly.
Below, we use unstated (https://github.com/jamiebuilds/unstated) to manage the state of our Playlist Controller. This allows us to access this playlist across our app, so we can access the currently playing item and the playlist everywhere. We chose to use unstated, but you could just as easily use any state management solution, such as redux, to store this information.
We can access our state globally by subscribing to it. For getting started with unstated, there’s some great examples on the repo homepage, linked above.
This state works offline, so when we subscribe to it, we won’t need to do any additional data fetching (other than the file itself). By persisting this state to async storage, we could even persist the state throughout app sessions.
We’ve now initialised the playlist controller, and we’ve wrapped the functionality we declared in the track player — play, pause etc. We also now want to add the actual playlist functionality, so we can add to the queue, keep track of the currently playing item and more. We first need to add a couple of more functions to our track player to manage the playlist additions.
After this, we can add the playlist management functions to our playlist controller.
We’re nearly there! We now have a way to maintain a playlist state, and skip between items in the playlist. Finally, we need to add a listener, so we can update the currently playing item on the track change. react-native-track-player provides us with a track-change listener that we can use. We need to add the ability to add the listeners to our Track Player first.
And now we can add the track-change listener to the playlist controller
We’re done! All that’s now required from us is to add items to the playlist, using our createPlaylistFrom function.
As you can see, using this format, we can manage the tracks in our playlist. You can now see how by using this controller, it’d be easy for a component to add to, or modify the playlist queue.
In this post we’ve shown you a rough outline about how you could manage a playlist state in react native — we’re well on the way to building a global audio player. However, we’d want a nice UI to manage all the functionality we’ve created in our controllers. In the next post, we’ll show you how to create a media player UI to show artwork, play / pause, skip through tracks, and present what’s playing to a user in a nice way.
Thanks for reading!
👏 (If you found this helpful, and would like to hear more, please feel free to give me some )