To avoid that the image eventually gets removed,
we need to store it in a more permanent path on our local device filesystem.
Of course, we can also upload it to a server but we already talked about service and so on,
here I want to focus on all the native device capabilities. Now to work with our native filesystem,
expo has got us covered,
we can use the filesystem package here
to do that. You install it just like you install the other native packages, with expo install
expo-file-system,
so let's do that here, expo install, again just a wrapper around npm install which you could alternatively
use, you
just need to make sure you're using the right version for your expo SDK version you're using and with
that, we have that installed,
now we can move the image after we took it.
Now there are various places in the app where we could do that,
we could do it in the image picker right after we took the image but at this point of time, we don't
know yet if we will actually submit the form, if we actually keep that image.
So what if we just took an image here but then we discard this and we go back?
I don't want to have the image moved to a permanent place in that case, it should definitely be cleaned
up and that's the default behavior, so I don't want to move it
at this point yet,
instead I want to move it once we submitted the form.
Hence we could do it here in the new place screen, in the save place handler
but then we would add all this filesystem logic to this component which is possible but which adds a
lot of logic into this component which I don't really want to have there,
I want to keep this component relatively lean.
A great place for this however is the action creator.
We already used this in the past for having side effects, like sending HTTP requests.
Now moving a file is basically the same category of thing we're doing, instead of sending a request to
a server,
we're moving a file,
well it's not that different.
So therefore in this file here, in the places-action.js file, I'll import everything as filesystem
from expo filesystem, so from this new package we installed and here in add place, I'll now use this alternative
syntax of dispatching or of having an action creator which utilizes Redux Thunk, where we return an internal
function here that receives the dispatch function as an argument so that in this internal function, we
can dispatch this action by calling dispatch here and passing our action object and I will add async
here so that we can use async await because we're going to do some asynchronous work here. In here,
I now want to move the file, right?
So for that, I want to use this filesystem package. Now moving the file actually involves a couple of
things.
First of all, we need to derive the new path of the file and that should of course be a more permanent
directory. For that, we can use filesystem and there, you've got a couple of directories you can access.
You got the cache directory which is actually the directory where the file is already stored in out
of the box, the bundle directory which is not really a good directory for storing files your app uses
either but you also got the document directory,
this is the main directory for any files that your app needs which are guaranteed to survive.
Now when you uninstall your app, this folder will also be erased,
so then these files are lost but until then, they will persist across app restarts, across long pauses
where people haven't used your app,
so here the files will survive.
So the filesystem document directory is the path I want to move my file to and now there's one important
thing to know, your path also needs to include the file name you want to use in the future.
Of course, you also get a temporary file name when you take the image but when you move a file, this name
is actually not kept out of the box, instead it will assume this path here as a name so to say. So therefore
this should not just be a pointer at the folder you want to move the file to,
it should instead also include the file name.
Now for that, I'm happy with keeping the autogenerated image name,
of course you could also now generate your own unique image names here but for that, I'll derive the file name
from the image which I got here,
keep in mind that image here is a path, the temporary path to the image.
So here, I'll actually use image which is a string and call split on this to split it by slashes because
this path of course involves a couple of slashes in the end and split converts this string into an array
of string segments where each segment is a segment before and after such a slash in the string
and there by calling pop,
I get the last segment.
So what this code here does is it takes a look at our image path, splits it by slashes which kind of
makes up our full path there and by popping the last element,
well what is the last element? That is our file name,
right, so if you have a path like somefolder/myimage.jpg, then what we do with
split is we get an array with some folder and with myimage.jpg and by calling pop on this,
we get myimage.jpg,
so that's what we get here.
So this will return us our file name,
this code snippet here and now we can append this here to our path by simply adding it like this.
So now this generates a path which both includes a folder and then the file name.
This is where I want to move that file to and now we can move it by accessing FileSystem.moveAsync.
That's a method which moves a file from a to b
and it also uses a promise because moving that file can take a bit longer and therefore it will tell
us when it's done.
Now move async takes an object with two pieces of information - from and to which is pretty straightforward
I guess.
So from is image because image is the path to the temporary directory, so that's the from thing, that's
where the file currently sits and to of course is our new path, like this. Now as I said, this returns a
promise
so we can await this and that's it.
Now we should wrap this into a try catch block because this could fail because for example there is
not enough space on the device or somehow we have a permissions error or anything else is wrong.
So we definitely should try catch our filesystem actions because operations on the filesystem can always
fail
and there we then might want to do something, here
I'm logging the error and I'm rethrowing it but of course you could do other things there as well,
you could kind of store this on your own analytics server, whatever you need to do.
Now you ultimately might want to handle this in a component too to show an alert there, I'll not do it but
you would handle it in the same way as you do for example handle HTTP errors which is something
I had a look at in the
HTTP module.
So here, we're assuming that this will mostly work or that this will always work and
we're moving the file. Now
once it was moved, we know it'll be in the new path,
so of course it's the new path which I now want to store here in my place
data or in my Redux store. That's all nice,
this should store our image in a permanent directory
but what we're not doing is we're not storing our data itself in a permanent place.
We're of course using Redux here and that means it's stored in memory but whenever we close and restart
our app, all our data will be lost because that's not stored on the device or on a server or in a database,
it's just in memory which is active as long as our app runs and thereafter that's cleared.
So as a next step, I want to show you how to use SQLite which is an on device database, both available
on iOS and Android, to store more than just files but to also store our data, like the title and
the image path and so on.