Save file to Documents directory with Swift
In this tutorial we will learn how to save a file to the Documents
directory. There are many reasons why this would be useful. You might want to save an image locally, I have written a tutorial about this before. You might want to build a file manager app etc.
We will be learning how to read and write to the Documents
directory, we will use Swift to create a new text file, we will then save that to the Documents
directory. After it is saved we will read the file to make sure that everything has worked as expected.
We are going to be creating four methods, documentDirectory
, append
, save
and read
. documentDirectory
will return a string path for our documentDirectory
. append
will allow us to append a component to a path. save
will allow to create and save a text file and read
will read a file in the Documents
directory.
Step 1: Create Documents directory path method
I always remember this as being an annoying task, but looking at it now, it really is a simple task.
Add the following method to your code:
private func documentDirectory() -> String {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory,
.userDomainMask,
true)
return documentDirectory[0]
}
There is not too much going on in this method. We look for the .documentDirectory
in the .userDomainMask
, using the NSSearchPathForDirectoriesInDomains
method. This method will return an array of strings, we only want the first element of the array which is why we do documentDirectory[0]
.
Step 2: Create Append to path method
The next method that we need to create is the append
method. This will allow us to append a component to our path. URL
already has appendPathComponent
but this will make a bit easier and cleaner for us later on.
Add the following method to your code:
private func append(toPath path: String,
withPathComponent pathComponent: String) -> String? {
if var pathURL = URL(string: path) {
pathURL.appendPathComponent(pathComponent)
return pathURL.absoluteString
}
return nil
}
This append
method will take two arguments, the first will be the path, the path is actually just a Documents
directory path. The second argument will be the the rest of the path, in our case this will be a file name.
Inside append
we will try and create a URL
from our path, if that does not return nil
we will append our pathComponent
to the URL
and return the absoluteString
from the URL
. If we cannot create a URL
from the path that we provide, we will return nil
.
Step 3: Create the read method
In this example of how to save to, and, read from the Documents
directory, our read method will only allow us to pass in a file name and we will not be allowed to change our "base" directory, we will only be reading from the Documents
directory.
Add the following read
to your code:
private func read(fromDocumentsWithFileName fileName: String) {
guard let filePath = self.append(toPath: self.documentDirectory(),
withPathComponent: fileName) else {
return
}
do {
let savedString = try String(contentsOfFile: filePath)
print(savedString)
} catch {
print("Error reading saved file")
}
}
As mentioned before, the read
method will only take the file name as an argument. We will try and get the filePath
for that that file name using the append
method. If the fileName
does not exist in the Documents
directory we return
.
If the Documents
directory does contain the file with the fileName
that we passed in we will try and read it. To read it we will initialise a new String
with the filePath
. If we successfully initialise a String
with the filePath
we will just print out the file content, print(savedString)
.
The contentsOfFile
initialiser for String
can throw an error, that is why we need to use the do try catch
. Generally by this point one would know if the file exists or not, but it might not be a format that a String
can be initialised with, because of this it will need to be able to throw an error.
Step 4: Save file to Documents directory
The last method that we need to implement is the save
method. The save
method will take three arguments. The first will be content of the text file. The second will be the directory, and the third will be the file name.
This method is quite similar to the read
method. Add the following method to your code:
private func save(text: String,
toDirectory directory: String,
withFileName fileName: String) {
guard let filePath = self.append(toPath: directory,
withPathComponent: fileName) else {
return
}
do {
try text.write(toFile: filePath,
atomically: true,
encoding: .utf8)
} catch {
print("Error", error)
return
}
print("Save successful")
}
We use the append method to add the fileName
to the directory
, which is just the base path to the Documents
directory.
The final part is to write the file. String
's have multiple write
methods, we have used the toFile
one, which allows us to pass in our filePath
as a String
.
Because writing to the file is not something that can be guaranteed, we need to wrap it in a do try catch
, otherwise we will print out the error.
Step 5: Call the save and read methods
Now that all the methods that we need have been created, we can all them. I am going to call them from viewDidLoad
.
This is what my viewDidLoad
looks like:
override func viewDidLoad() {
super.viewDidLoad()
let fileName = "testFile1.txt"
self.save(text: "Some test content to write to the file",
toDirectory: self.documentDirectory(),
withFileName: fileName)
self.read(fromDocumentsWithFileName: fileName)
}
I have created a let
for the fileName
because it is used in two places. Then I call the save
method which will write "Some test content to write to the file"
to a file, and then after the save
method I call the read
method. All the read method does is print out the contents of the file that we saved.
Conclusion
As I mentioned before, I remember writing to files being a pain, but it is actually quick an easy to read and write files.
If you want to look at the final source code, it can be found here.