Swift File Manager: Reading, Writing, and Deleting Files and Directories

Welcome to the world of Swift file management! I’m going to show you how to store data in your iOS app. Learn how to read, delete, and write data to files and directories using FileManager in Swift.

In this guide, I’ll walk you through the ins and outs of FileManager, show you how to navigate file paths, and even teach you how to create and manage file URLs. I will show you an example, where I save data in a SwiftUI app. It is a simple reading list app.

Hey there, welcome to this exciting journey into the world of Swift file management! Today, I’m going to show you how to store data in your iOS app. Learn how to read, delete, and write data to files and directories using FileManager in Swift.

In this guide, I’ll walk you through the ins and outs of FileManager, show you how to navigate file paths, and even teach you how to create and manage file URLs. I will show you an example, where I save data in a SwiftUI app. It is a simple reading list app.

⬇️ You can find the project files on my Github here.

The sample project also uses JSON and Codable to transfer the data to file. In the second part of this tutorial, you will learn more about the JSON file format and how to use the Codable protocol. Have a look here: Codable: How to simplify converting JSON data to Swift objects and vice versa.

Understanding FileManager and File Paths in Swift

Think of FileManager as your personal assistant when it comes to dealing with files. It’s a built-in Swift class that lets you easily access the file system and is part of the foundation framework. You can use the file manager class to do things like create a directory, delete a file, or check if a file exists. It’s like your Swiss Army knife for file operations. You can access the file manager by getting the shared file manager object:

let fileManager = FileManager.default

The file manager is a unique instance.

Now, onto file paths. In the real world, a path is a trail that leads you to a specific location, right? In Swift, a file path is pretty much the same thing – it’s a string that points to the location of a file or directory in the file system.

For example, let’s say you have a file called “groceryList.txt” in a directory called “Documents”. The file path might look something like this:

"/Users/YourName/Documents/groceryList.txt"

This path tells Swift exactly where to find your grocery list file.

But here’s the cool part. Instead of dealing with messy string paths, Swift uses URLs to represent file paths. Why? Because URLs provide a standard, structured way to locate resources, whether they’re on the web or in your app’s file system.

In Swift, you’ll work with file paths as URLs. And FileManager is going to be your best friend in this process, helping you create, read, and manage these URLs.

Free SwiftUI Layout Book

Get Hands-on with SwiftUI Layout.

Master SwiftUI layouts with real-world examples and step-by-step guide. Building Complex and Responsive Interfaces.

Sandbox Environment and Document Directory: How does the file system work on iOS?

In iOS, each app operates within its own secure environment, known as a sandbox. The sandbox is a set of controls that restricts the app’s access to files, preferences, network resources, hardware, and so on. The purpose of the sandbox is to provide security and to prevent apps from accessing data they shouldn’t have access to.

sandbox environment on iOS creates a secure location for your apps data

When it comes to file management, each app has its own sandboxed file system. This means that each app has its own area in the device’s file system where it can save and manage files. Other apps can’t access this area, which helps keep the app’s data secure.

One of the key parts of an app’s sandboxed file system is the Documents directory. This is a directory where an app can save user-generated content or other data that the app needs to function properly. The Documents directory is automatically created by the system when the app is installed.

Here’s how you can get the URL for the Documents directory in Swift:

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

In above code, FileManager.default.urls(for:in:) returns an array of URLs for the specified directory in the specified domain. The .documentDirectory argument specifies that we want the Documents directory, and the .userDomainMaskargument specifies that we want the current user’s home directory. The first! at the end gets the first (and usually only) URL in the array.

Once you have the URL for the Documents directory, you can use it to save and load files. However, keep in mind that all file operations are subject to the sandbox’s access controls. For example, your app can’t access or modify files in another app’s Documents directory.

Working with Directories in Swift’s File System

Alright, now that you’ve got a handle on FileManager and file paths, let’s move on to working with directories in Swift’s file system.

Let’s say you’re working on an app that needs to store user photos. Instead of dumping all the photos directly into the documents directory, you decide to create a separate directory called “UserPhotos”. Here’s how you’d do it:

let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let userPhotosURL = documentsURL.appendingPathComponent("UserPhotos")

do {
    try FileManager.default.createDirectory(at: userPhotosURL, withIntermediateDirectories: true, attributes: nil)
} catch {
    print("Error creating directory: \(error)")
}

In this code, I’m first getting the URL for the documents directory from the file manager object. Then, I’m creating a new URL for the “UserPhotos” directory by appending “UserPhotos” to the document’s URL. Finally, I’m asking FileManager to create this new directory.

However, if the “UserPhotos” directory exists, an error occurs. The createDirectory method will throw an error with the FileManager.Error.Code value .fileWriteFileExists. In this case, the execution will jump to the catch block, and the error message “Error creating directory: file already exists” (or similar) will be printed.

It’s important to handle such errors appropriately in your code, either by gracefully handling the situation or providing appropriate feedback to the user.

Creating and Managing File URLs in Swift

Great job so far! Now that you’ve got directories down, let’s move on to creating and managing file URLs in Swift.

Remember the office building analogy? Well, if directories are rooms, then files are the documents in those rooms. And just like how each document has a specific spot in a room, each file in your app has a specific URL.

Let’s say you want to create a new file called “userProfile.txt” in the “UserPhotos” directory you created earlier. Here’s how you’d do it:

let userProfileURL = userPhotosURL.appendingPathComponent("userProfile.txt")

In this line of code, I’m creating a new URL for the “userProfile.txt” file by appending “userProfile.txt” to the “UserPhotos” directory URL. And just like that, you’ve got a URL for your new file!

But what if you want to add a file extension? No problem! You can do that by using the `appendingPathExtension` method, like this:

let userProfileURL = userPhotosURL.appendingPathComponent("userProfile.txt")

This code does the same thing as the previous example, but it separates the file name (“userProfile”) and the file extension (“txt”).

And there you have it! You now know how to create file URLs in Swift. In the next sections, I’ll show you how to perform file operations like reading, writing, and deleting files.

Writing to Files in Swift: Navigating the File System

Now that you know how to create file URLs, let’s dive into writing data to files in Swift.

Imagine you’re writing a letter. You’ve got your pen (the data), and your envelope (the file URL). Now, you just need to write your letter (the data) and put it in the envelope (the file URL). In Swift, you do this using the write(to:) method.

Let’s say you want to write the string “Hello, World!” to the “userProfile.txt” file you created earlier. Here’s how you’d do it:

let helloWorldString = "Hello, World!"

if let data = helloWorldString.data(using: .utf8) {
    do {
        try data.write(to: userProfileURL)
        print("Successfully wrote to file!")
    } catch {
        print("Error writing to file: \(error)")
    }
}

In this code, I’m first converting the “Hello, World!” string to data. Then, I’m writing this data to the “userProfile.txt” file using the write(to:) method. If everything goes well, “Successfully wrote to file!” will be printed. If there’s an error, it’ll print “Error writing to file:” followed by the error.

Reading from Files in Swift: Accessing the Documents Directory

Fantastic! You’ve written data to a file. Now, let’s flip the script and read data from a file in Swift.

Think of it like this: you’ve sent your letter (written data to a file), and now you’re receiving a letter (reading data from a file). In Swift, you do this using the Data(contentsOf:) initializer.

Let’s say you want to read the data from the “userProfile.txt” file you wrote to earlier. Here’s how you’d do it:

do {
    let data = try Data(contentsOf: userProfileURL)
    if let string = String(data: data, encoding: .utf8) {
        print("File contents: \(string)")
    }
} catch {
    print("Error reading file: \(error)")
}

In this code, I’m first creating a new Data object from the contents of the “userProfile.txt” file. Then, I’m converting this data back into a string. If everything goes well, it’ll print “File contents: ” followed by the contents of the file. If there’s an error, it’ll print “Error reading file:” followed by the error.

Free SwiftUI Layout Book

Get Hands-on with SwiftUI Layout.

Master SwiftUI layouts with real-world examples and step-by-step guide. Building Complex and Responsive Interfaces.

Deleting Files in Swift: Utilizing File Manager

Let’s learn how to delete files in Swift. Think of it like cleaning up your room. Sometimes, you need to get rid of old stuff (files) to make room for new stuff. In Swift, you do this using the removeItem(at:) method of FileManager.

Let’s say you want to delete the “userProfile.txt” file you’ve been working with. Here’s how you’d do it:

do {
    try FileManager.default.removeItem(at: userProfileURL)
    print("Successfully deleted file!")
} catch {
    print("Error deleting file: \(error)")
}

In this code, I’m asking FileManager to delete the “userProfile.txt” file. If everything goes well, it’ll print “Successfully deleted file!”. If there’s an error, it’ll print “Error deleting file:” followed by the error.

Saving a more Complex Data Object in SwiftUI

If your data is more complex and goes beyond simple strings, you can use various techniques to save more complex data structures to a file. One commonly used approach is to leverage serialization and deserialization using formats like JSON or Property List (plist file). These formats allow you to convert your complex data structures into a format that can be saved to a file and easily reconstructed later.

As an example, I am going to use a simple reading list app. You can see all you saved web pages in a list. There is a plus button to add new web pages and add a title. When I tap on one item in the reading list, it opens a detail view and loads the web pages. As you can see saving the reading web pages to file is a major part of the app.

swiftui demo project to store user data as json with Codable.
This Reading List app stores the users data as JSON on their iPhone.

The data is more complex than one single string. Here’s a general approach using JSON as an example:

  1. Define a Swift data model that represents your complex data structure. This model should conform to the Codable protocol, which enables encoding and decoding to/from JSON.
  2. Use the JSONEncoder class to encode your data model into JSON data. This data can then be saved to a file.
  3. To read the data back, use the JSONDecoder class to decode the JSON data from the file into your data model.

Let’s illustrate this with my reading list app example. Assume you have a data model called ReadingData representing a person’s name and age:

struct ReadingData: Codable {
    let url: URL?
    let title: String
    let creationDate: Date
    var hasFinishedReading: Bool
}

To save an array of ReadingData instances to a file, you can use the following code:

let readingData = [ReadingData(url: URL(string: "https://www.apple.com/"),
                    title: "apple",
                    creationDate: Date(),
                    hasFinishedReading: false)]
// construct a file path using FileManager
let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let fileURL = documentsDirectory.appendingPathComponent("readingListData.json")

do {
    let jsonEncoder = JSONEncoder()
    let jsonData = try jsonEncoder.encode(readingData)

    // Save jsonData to a file
    try data.write(to: fileURL)
} catch {
    print("Error encoding data: \(error)")
}

To read the data back from the file:

do {
    // Retrieve jsonData from the file using FileManager
    let jsonData = try Data(contentsOf: fileURL)

    let jsonDecoder = JSONDecoder()
    let readingData = try jsonDecoder.decode([ReadingData].self, from: jsonData)

    // Use the `readingData` array in your app
    // ...
} catch {
    print("Error decoding data: \(error)")
}

By leveraging encoding and decoding with formats like JSON or Property List, you can save more complex data structures, including nested objects and arrays, to files in Swift.

If you want to learn more about JSON files and Codable, check out this tutorial Codable: How to simplify converting JSON data to Swift objects and vice versa.

I only save the data to one specific file. My data is only small and not very complex. I hard coded the file url. If you have a note taking app, then it would make more sense to store one file per note. You would also need to identify files for all your notes.

Conclusion: Mastering the File Manager in Swift

Mastering file management in Swift using FileManager opens up a world of possibilities for handling files and directories in your iOS apps. By understanding how to read, write, and delete files, as well as create and manage directories, you have the power to efficiently handle user data and enhance the user experience. With the knowledge gained, you can confidently navigate the file system, organize data, and ensure the security and persistence of user information. So go ahead, explore the capabilities of FileManager, experiment with different file operations, and take your Swift development skills to the next level. Happy coding!

How to go from here:

1 thought on “Swift File Manager: Reading, Writing, and Deleting Files and Directories”

  1. How does one count the number of files, say JSON files in a given directory?

    How does one read multiple files in a given directory, say for the purposes of extracting similar data from multiple JSON files to create a list of values

    Reply

Leave a Comment

Subscribe to My Newsletter

Want the latest iOS development trends and insights delivered to your inbox? Subscribe to our newsletter now!

Newsletter Form

New Course Announcement!!!

50% OFF Launch Sale

MacOS Development with SwiftUI