Save Images Locally with Swift 5
This page has moved to programmingwithswift.com
Depending on what you are building, it could be useful to save images locally. In this tutorial I will show you the basics of how to save an image to UserDefaults
as well as to the file system
. You will be able to use the same technique to save an image to Core Data
, but I will not be showing you how to do that.
Why save images locally?
There are many reasons for wanting to save images locally on a users device. You might want to save the images so that you do not need to download them again, or, you might be building an app that edits images. These are only a few of the reasons that you may want to save images locally.
Base implementation
Like I mentioned before, we are going to build an app that allows us to save an image locally. We will be using UserDefaults
and file system
in order to save locally. To get started we will start by writing the foundational code and then later on we will add the implementation code for each method of saving the images.
Lets start by creating the StorageType
enum. This enum will have two cases, userDefaults
and fileSystem
. This will be used in the store
and retrieveImage
functions that we will be creating later on.
|
|
Now that we have the storage types we can start creating the foundations of the store
and retrieveImage
functions.
The store
function will take three parameters. image
, this will be of type UIImage
, key
, this will be of type String
and will be a unique name for the image we want to save/retrieve, and finally we will pass through the storageType
.
|
|
We now have our function. We should be able to add some of the base logic to it now. In this function we want to take a UIImage
and convert it to Data
. This will make it much easier for use to store the image. We also want to use the correct file storage. Update your store
function with the following
|
|
We have now created the foundations of our store
function. Later on we will implement each of the saving methods, but for now we are going to move on to getting the foundational code written for our retrieve
function.
The retrieveImage
function is going to take two arguments. The first argument will be, key
, which will be the same as the key
in the store
function. The second argument will be storageType
, again, this will be used in the same way as it is used in the store
function.
|
|
And that is all that is needed for the foundation code. We can now start implementing the UserDefaults
save and retrieve functionality.
Saving and retrieving image with UserDefaults
The UserDefaults
is the easier way to save the images. In our code that we have written so far we have already converted our UIImage
to Data
. All we need to do now is to save and retrieve it.
Lets do the saving first. To save the data to UserDefaults
update your store
method to look like the below code.
|
|
That was quite easy, a nice one liner to save the image when using UserDefaults
. Ok, now lets implement the retrieveImage
functionality for UserDefaults
.
|
|
So that was almost as simple as storing the image. Because UserDefaults
stores Data
as Any
type, we need to cast it back to Data
when we retrieve it. Once we do that, we instantiate a new UIImage
with the imageData
and then we return that UIImage
that we initialised.
Save and retrieve image with File System
Using the file system
is quite a bit more complicated than using UserDefaults
. We are going to start off by writing a small helper method called filePath
. filePath
will only take one argument called key
. The key
argument is the same key
that we use in other places in our code.
|
|
This filePath
method doesn’t do too much, but it will help out later on. Basically all this method will do is get the url for the home directory on the device. It will then append the key
.png to the url and return that value. I have used the png extension in this example as we are only working with png data in this example.
Ok, now that we have our helper function we can implement the .fileSystem
functionality in the store
function. Update your store
function to look like the below function.
|
|
Since the filePath
function returns an optional URL
we need to unwrap it before we can use it. Once we have unwrapped it we need to write the data to that filePath. Luckily for us Data
has a write
method which we can use to write the data to a file. This is where we will use the filePath that our helper method has returned. The write
method can throw an error so we need to make sure do wrap it in a do catch
. You should now be able to write the image data to disk.
This was a little bit more complicated but not too much more complicated, so lets move straight into the retrieveImage
function.
Update your retrieveImage
function to look like the below:
|
|
Once again we start off by using our filePath
helper method to return the URL
to the file that we have stored. Once we have that URL
we ask the FileManager
to get the contents of that URL
. Now that we have the file data we can use that data to instantiate a new UIImage
. If none of the above produces a nil
value we can return our image that we retrieved from disk.
That is it when it comes to the basics. You should now be able to read and write an image to the file system
as well as to UserDefaults
Creating test UI
Now that we have all the functional stuff done we can test that everything is working as expected. To do this I have updated my Main.storyboard
to look like the following:
Once you have done that, created outlets for the two UIImageView’s and the two UIButton’s. My outlets look like this:
|
|
If you add this code you will not be able to build your project. So to fix that problem we need to implement those two functions, save
and display
. The save
function will call the store
function that we created earlier and the display
function will call the retrieveImage
function and then display the image that was returned, either from the fileSystem
or from UserDefaults
. These two functions look like this:
|
|
I have wrapped calling the store
and the retrieveImages
in DispatchQueue
’s because if you don’t then it will block the main thread.
Awesome, now that everything is hooked up you should be able to build and run the app. You will need to update the save
and display
method depending on where you want to save an image. Oh, you will also need to change the image name. I used an image called buildingImage
, but this will not work for you. To get an image into your project you can drag any image into your Assets.xcassets
and then you will need to replace buildingImage
with whatever the name of your image is.