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 helpFileManager
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.
URL
thing?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 URL
s.
FileManager
to have a documentsURL
-propertyTo 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
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.