Introduction

Saving data locally in the digital world means writing data to a file on the devices systems. Files are stored in folders on said systems. But we don't have a folder structure on iOS as we have it on macOS (or on any other operating system) -- so where do we save data?

We could be using Core Data, which is a framework Apple provides us with to store data locally in a SQLite database. But perhaps we are planning on using our own NoSQL server backend in the end and for that reason want to save JSON files locally right from the start?

We could also use iOS's UserDefaults, which enables us to store key-value pairs locally, but that framework is not supposed to be used to persist data -- it is rather meant to be used to store a minimal amount of informations such as the currently logged in username or other informations on states of the app (and you don't want to use it to store a user's password in any case because of security reasons!).

So, we do want to get access to a means enabling us to write informations to a file somewhere on the iOS device in a way that it can be read at a later stage.

FileManager to the help

FileManager is an iOS class in Foundation, that enables you to work with folders and files on your iOS device. Because applications on iOS are sandboxed, there aren't too many places where data can be stored. A good place to store data though is the user's documents directory within the application's sandbox.

Accessing the user's documents directory can be done like so:

let documentsURL = FileManager.default.urls(for: .documentsDirectory, in: .userDomainMask).first

In prose, above code means, that we are accessing the default instance of FileManager (which is a singleton) to access its method urls(for:in:). Within the for value we tell it to access the documents directory, with the in value we demand access to the user's home folder. Because urls(for:in:) results in an array of URLs , but we only need the first one, we can use subscripting ([0]) to access the first element in this array. Note that we cannot use the first property of urls because that is an optional and we don't want documentsURL to be an optional -- of course we could force unwrap or securely unwrap with if let, but it seems to me that this is not as swifty as we want it to be, so we stick to subscripting for now.

What is this URLthing?

According to Apple's documentation, URL is a struct, that

[...] identifies the location of a resource, such as an item on a remote server or the path to a local file.

So, plainly put, as soon as you are working with files -- be it locally or remote -- you are dealing with URLs.

Extending FileManager to have a documentsURL-property

To be able to access the user's documents folder more consistently in your project and not have to instantiate a new object over and over again, we can create our own littel extension on FileManager. To do so, I recommend creating a new folder in your project named extensions. Within that folder, we create a new File called FileManager.swift with the following code:

import Foundation

public extension FileManager {
    static var documentsDirectory: URL {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
    }
}

We specify the extension to be public, so that we can have access to it from everywhere in our project. We than create a computed property documentsDirectory, which is static, so that it's not re-instantiated again with each call, following the singleton pattern.

With this extension in place, we can now get the document's directory be just calling

let docDir = FileManager.documentsDirectory

What's next?

In the next blogpost in this series, I will proceed to write data to disk and read again from disk using the user's document directory.

Previous Post Next Post