My Cordova nightmares: Accessing photos from the phone’s gallery
Gallery API, Promises, WKWebView, Cordova local webserver
You want your users to pick photos that they shot earlier? The good news: You can do this with Cordova. The bad news: Getting there will be nightmarish.
Here is what we learned on our — rather nightmarish — trip.
Gallery API
The Cordova-Gallery-API is a great library that provides access to the phone’s gallery. After some experimentation, we found SuryaL’s fork to be the most stable and feature-rich: https://github.com/SuryaL/cordova-gallery-api
These are the commands for a standard scenario:
- Find all albums on the phone (e.g. Camera Roll) via
galleryAPI.getAlbums()
- Find all media items within one album via
galleryAPI.getMedia()
- Request the thumbnail for one media item via
galleryAPI.getMediaThumbnail()
- Request the high quality version of one media item via
galleryAPI.getHQImageData()
Escaping callback hell
In best Cordova tradition, combining the calls catapults the programmer quickly into callback hell:
galleryAPI.getAlbums(
function(albums) {
albums.forEach(function(album) {
galleryAPI.getMedia(
album,
function(media) {
...
},
function(e) {
throw new Error(e);
}
);
}
}),
function(e) {
throw new Error(e);
}
);
We clearly don’t want to end up there. Luckily, there is an easy solution. Wrapping the gallery-api with promises let’s us sneak out of callback hell:
const getAlbums = () =>
new Promise((resolve, reject) => {
galleryAPI.getAlbums(
albums => resolve(albums),
e => reject(`Failed to get albums: ${e}`)
);
});
const getMedia = (album) =>
new Promise((resolve, reject) => {
galleryAPI.getMedia(
album,
items => resolve(items),
e => reject(`Failed to load items for album ${album.id}: ${e}`)
);
});
With promises in place, combining the calls reads very natural:
getAlbums()
.then(albums => getMedia(album))
.then(items => getMediaThumbnails())
Burned by WKWebView
But there was one more proof for us: For Android, we can simply stick the thumbnail URL returned by Gallery API into a <img>
tag to show the file to the user. But WKWebView, the high performance WebView for iOS, refuses to load the photo from the device.
The trick is to install Cordova’s local webserver plugin and capture the cdvToken passed to your index.html as a parameter (e.g. index.html?cdvToken=xyz
):
// Using https://medialize.github.io/URI.js/
const cdvToken = URI(window.location).query(true).cdvToken
Now, you just need to prefix the thumbnails provided by Gallery API with /local-filesystem
, add the cdvToken, and your images are finally ready to render!
items.forEach(item => {
html += `<img src="/local-filesystem${item.thumbnail}?cdvToken=${cdvToken}"></img>`;
});
Solution - out of the box
Sounds complicated? Agreed. We therefore collected all pieces of the puzzles into a library and open sourced it: https://github.com/Collaborne/cordova-gallery-access
Check it out, and let us know what you think.
Happy coding!
Want to learn more about coding? Have a look to our other articles.
Photo: gags9999