File System CRUD operations manager


Greetings, traveler!

iOS File System is a powerful tool for storing data. However, it is not the only option since Apple offers other tools for different scenarios.

Note

By the way, if you want to read more about these options, you can do so here.

If your app frequently uses the File System, you may want to create a universal tool to perform CRUD operations. So, let’s do this.

Create a class FileSystemManager.

final class FileSystemManager {

}

Now, let’s enhance it.

File path

First, we need to specify the URL to perform any operation. To do this, we need to know three parameters.

  1. Key — a string path component.
  2. Directory — a directory which will be used.
  3. Domain mask — domain constants specifying base locations to use when you search for significant directories. We will use the user’s home directory by default.

Note

By the way, if you want to read more about FileManager directories, you can check out this article.

Now, let’s create a private method. This method will be throwable and return a URL. We will specify the document directory, append our custom path component, and return this value.

private func filePath(
    key: String,
    directory: FileManager.SearchPathDirectory,
    domainMask: FileManager.SearchPathDomainMask
) throws -> URL {
    let documentDirectory = try FileManager.default.url(
        for: directory,
        in: domainMask,
        appropriateFor: nil,
        create: false
    )
    
    return documentDirectory.appendingPathComponent(key)
}

Save

Now, we can perform CRUD operations. Let’s start with saving. We can only use objects that conform to the Codable protocol, so we can use a generic method. We will use the same parameters but with an additional one — the first parameter will be the data to save.

We will create JSON data with the JSONEncoder and write it using the desired file path.

func save<T: Codable>(
    _ data: T,
    key: String,
    directory: FileManager.SearchPathDirectory,
    domainMask: FileManager.SearchPathDomainMask = .userDomainMask
) throws {
    let filePath = try filePath(
        key: key,
        directory: directory,
        domainMask: domainMask
    )
    let jsonData = try JSONEncoder().encode(data)
    
    try jsonData.write(to: filePath)
}

Retrieve

To retrieve an object, we will use our filePath method once again. After retrieving the contents of the provided file path, we can use JSONDecoder to decode it and return its value.

func retrieve<T: Codable>(
    _ type: T.Type,
    key: String,
    directory: FileManager.SearchPathDirectory,
    domainMask: FileManager.SearchPathDomainMask = .userDomainMask
) throws -> T {
    let filePath = try filePath(
        key: key,
        directory: directory,
        domainMask: domainMask
    )
    let jsonData = try Data(contentsOf: filePath)
    
    return try JSONDecoder().decode(T.self, from: jsonData)
}

Delete

This is the most straightforward method. After the file path is specified, we just need to call the FileManager removeItem method.

func delete(
    key: String,
    directory: FileManager.SearchPathDirectory,
    domainMask: FileManager.SearchPathDomainMask = .userDomainMask
) throws {
    let filePath = try filePath(
        key: key,
        directory: directory,
        domainMask: domainMask
    )
    
    try FileManager().removeItem(at: filePath)
}

Examples of usage

That’s it! Now, we have a universal tool for CRUD operations. Let’s try it out.

let manager = FileSystemManager()

// Save
try? manager.save("Test", key: "test", directory: .cachesDirectory)

// Retrieve
let object = try? manager.retrieve(
    String.self,
    key: "test",
    directory: .cachesDirectory
)
print(object) // Test

// Delete
try? manager.delete(key: "test", directory: .cachesDirectory)

Conclusion

You can enhance this tool of your choice. Anyway, if you want to check out the source code or use this tool as a framework in your project, you can do so on my GitHub.