Working with Directories in Swift on iOS 17

It is sometimes easy to forget that iOS is an operating system much like that running on many other computers today. Given this fact, it should come as no surprise that iOS has a file system much like any other operating system allowing apps to store persistent data on behalf of the user. Again, much like other platforms, the iOS file system provides a directory-based structure into which files can be created and organized.

Since the introduction of iOS 5, the iOS app developer has had two options in terms of storing data. Files and data may now be stored on the file system of the local device or remotely using Apple’s iCloud service. In practice, however, it is most likely that an app will utilize iCloud storage to augment, rather than replace, the local file system, so familiarity with both concepts is still necessary.

The topic of iCloud-based storage will be covered in detail, beginning with the chapter entitled Preparing an iOS 17 App to use iCloud Storage. The goal of this chapter, however, is to provide an overview of how to work with local file system directories from within an iOS app. Topics covered include identifying the app’s document and temporary directories, finding the current working directory, creating, removing, and renaming directories, and obtaining listings of a directory’s content. Once the topic of directory management has been covered, we will move on to handling files in Working with Files in Swift on iOS 17.

The Application Documents Directory

An iPhone or iPad user can install multiple apps on a single device. However, the iOS platform ensures that these apps cannot interfere with each other regarding memory usage and data storage. Each app is restricted in where it can store data on the device’s file system. iOS achieves this by allowing apps to read and write only to their own Documents and tmp directories. Within these two directories, the corresponding app can create files and subdirectories to any required level of depth. This area constitutes the app’s sandbox, and the app cannot usually create or modify files or directories outside of these directories unless using the UIDocumentPickerViewController class.

The FileManager, FileHandle, and Data Classes

The Foundation Framework provides three classes that are indispensable when it comes to working with files and directories:

 

You are reading a sample chapter from Building iOS 17 Apps using Xcode Storyboards.

Buy the full book now in eBook or Print format.

The full book contains 96 chapters and 760 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

  • FileManager – The FileManager class can perform basic file and directory operations such as creating, moving, reading, and writing files and reading and setting file attributes. In addition, this class provides methods for, amongst other tasks, identifying the current working directory, changing to a new directory, creating directories, and listing the contents of a directory.
  • FileHandle – The FileHandle class is provided for performing lower-level operations on files, such as seeking to a specific position in a file and reading and writing a file’s contents by a specified number of byte chunks, and appending data to an existing file. This class will be used extensively in the Working with Files in Swift on iOS 17 chapter.
  • Data – The Data class provides a storage buffer into which the contents of a file may be read, or dynamically stored data may be written to a file.

Understanding Pathnames in Swift

As with macOS, iOS defines pathnames using the standard UNIX convention. Each path component is separated by a forward slash (/). When an app starts, the current working directory is the file system’s root directory represented by a single /. From this location, the app must navigate to its own Documents and tmp directories to be able to write files to the file system. Path names that begin with a / are said to be absolute path names in that they specify a file system location relative to the root directory. For example, /var/mobile is an absolute path name.

Paths that do not begin with a slash are interpreted to be relative to a current working directory. So, for example, if the current working directory is /User/demo and the path name is mapdata/local.xml, then the file is considered to have an equivalent full, absolute pathname of /User/demo/mapdata/local.xml.

Obtaining a Reference to the Default FileManager Object

The FileManager class contains a property named default that is used to obtain a reference to the app’s default file manager instance:

let filemgr = FileManager.default

Having obtained the object reference, we can begin to use it to work with files and directories.

Identifying the Current Working Directory

As previously mentioned, when an app first loads, its current working directory is the app’s root directory, represented by a / character. The current working directory may be identified at any time by accessing the currentDirectoryPath property of the file manager object. For example, the following code fragment identifies the current working directory:

 

You are reading a sample chapter from Building iOS 17 Apps using Xcode Storyboards.

Buy the full book now in eBook or Print format.

The full book contains 96 chapters and 760 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

let currentPath = filemgr.currentDirectoryPath

Identifying the Documents Directory

Each iOS app on a device has its own private Documents and tmp directories into which it can read and write data. Because the location of these directories is different for each app, the only way to find the correct path is to ask iOS. The exact location will also differ depending on whether the app is running on a physical iPhone or iPad device or in the iOS Simulator. The Documents directory for an app may be identified by making a call to a file manager method named urls(for:), passing through an argument (in this case .documentDirectory) indicating that we require the path to the Documents directory. The .userDomainMask argument indicates to the urls(for:) method that we are looking for the Documents directory located in the app’s home directory. The method returns an object in the form of an array containing the results of the request. We can, therefore, obtain the path to the current app’s Documents directory as follows:

let filemgr = FileManager.default

let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)

let docsDir = dirPaths[0].path

To access the path string contained within the URL, we access the path property of the URL at index position 0 in the array and assign it to the docsDir constant as outlined above.

When executed within the iOS Simulator environment, the path returned will take the form of the following:

/Users/<user name>/Library/Developer/CoreSimulator/Devices/<device id>/data/Containers/Data/Application/<app id>/Documents

Where <user name> is the name of the user currently logged into the macOS system on which the simulator is running, <device id> is the unique ID of the device on which the app is running, and <app id> is the unique ID of the app, for example:

06A3AEBA-8C34-476E-937F-A27BDD2E450A

Clearly, this references a path on your macOS system, so feel free to open up a Finder window and explore the file system sandbox areas for your iOS apps.

 

You are reading a sample chapter from Building iOS 17 Apps using Xcode Storyboards.

Buy the full book now in eBook or Print format.

The full book contains 96 chapters and 760 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

When executed on a physical iOS device, however, the path returned by the function call will take the following form:

/var/mobile/Containers/Data/Application/<app id>/Documents

Identifying the Temporary Directory

In addition to the Documents directory, iOS apps are also provided with a tmp directory for storing temporary files. The path to the current app’s temporary directory may be ascertained with a call to the NSTemporaryDirectory C function as follows:

let tmpDir = NSTemporaryDirectory()

Once executed, the string object referenced by tmpDir will contain the path to the temporary directory for the app.

Changing Directory

Having identified the path to the app’s document or temporary directory, the chances are that you will need to make that directory the current working directory. The current working directory of a running iOS app can be changed with a call to the changeCurrentDirectoryPath method of a FileManager instance. The destination directory path is passed as an argument to the instance method in the form of a String object. Note that this method returns a Boolean true or false result to indicate whether the requested directory change was successful. A failure result typically indicates either that the specified directory does not exist or that the app lacks the appropriate access permissions:

let filemgr = FileManager.default

let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)

let docsDir = dirPaths[0].path

if filemgr.changeCurrentDirectoryPath(docsDir) {
    // Success
} else {
    // Failure
}

In the above example, the path to the Documents directory is identified and then used as an argument to the changeCurrentDirectoryPath method of the file manager object to change the current working directory to that location.

 

You are reading a sample chapter from Building iOS 17 Apps using Xcode Storyboards.

Buy the full book now in eBook or Print format.

The full book contains 96 chapters and 760 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

Creating a New Directory

A new directory on an iOS device is created using the createDirectory(atPath:) instance method of the FileManager class, once again passing through the pathname of the new directory as an argument and returning a Boolean success or failure result. The second argument to this method defines whether any intermediate directory levels should be created automatically. For example, if we wanted to create a directory with the path /var/mobile/Containers/Data/Application/<app id>/Documents/mydata/maps and the mydata subdirectory does not yet exist, setting the withIntermediateDirectories argument to true will cause this directory to be created automatically before then creating the maps sub-directory within it. If this argument is set to false, then the attempt to create the directory will fail because mydata does not already exist, and we have not given permission for it to be created on our behalf.

This method also takes additional arguments in the form of a set of attributes for the new directory. Specifying nil will use the default attributes.

The following code fragment identifies the documents directory and creates a new sub-directory named data in that directory:

let filemgr = FileManager.default

let dirPaths = filemgr.urls(for: .documentDirectory, in: .userDomainMask)

let docsURL = dirPaths[0]

let newDir = docsURL.appendingPathComponent("data").path

do {
    try filemgr.createDirectory(atPath: newDir,
                withIntermediateDirectories: true, attributes: nil)
    } catch let error as NSError {
            print("Error: \(error.localizedDescription)")
}

Deleting a Directory

An existing directory may be removed from the file system using the removeItem(atPath:) method, passing through the directory’s path to be removed as an argument. For example, to remove the data directory created in the preceding example, we might write the following code:

do {
    try filemgr.removeItem(atPath: newDir)
} catch let error {
    print("Error: \(error.localizedDescription)")
}

Listing the Contents of a Directory

A listing of the files contained within a specified directory can be obtained using the contentsOfDirectory(atPath:) method. This method takes the directory pathname as an argument and returns an array object containing the names of the files and sub-directories in that directory. The following example obtains a listing of the contents of the root directory (/) and displays each item in the Xcode console panel during execution:

 

You are reading a sample chapter from Building iOS 17 Apps using Xcode Storyboards.

Buy the full book now in eBook or Print format.

The full book contains 96 chapters and 760 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 

do {
    let filelist = try filemgr.contentsOfDirectory(atPath: "/")

    for filename in filelist {
        print(filename)
    }
} catch let error {
    print("Error: \(error.localizedDescription)")
}

Getting the Attributes of a File or Directory

The attributes of a file or directory may be obtained using the attributesOfItem(atPath:) method. This takes as arguments the path of the directory and an optional Error object into which information about any errors will be placed (may be specified as nil if this information is not required). The results are returned in the form of an NSDictionary dictionary object. The keys for this dictionary are as follows:

NSFileType
NSFileTypeDirectory
NSFileTypeRegular
NSFileTypeSymbolicLink
NSFileTypeSocket
NSFileTypeCharacterSpecial
NSFileTypeBlockSpecial
NSFileTypeUnknown
NSFileSize
NSFileModificationDate
NSFileReferenceCount
NSFileDeviceIdentifier
NSFileOwnerAccountName
NSFileGroupOwnerAccountName
NSFilePosixPermissions
NSFileSystemNumber
NSFileSystemFileNumber
NSFileExtensionHidden
NSFileHFSCreatorCode
NSFileHFSTypeCode
NSFileImmutable
NSFileAppendOnly
NSFileCreationDate
NSFileOwnerAccountID
NSFileGroupOwnerAccountID

For example, we can extract the file type for the /Applications directory using the following code excerpt:

let filemgr = FileManager.default

do {
    let attribs: NSDictionary =
           try filemgr.attributesOfItem(atPath: "/Applications") as NSDictionary
    let type = attribs["NSFileType"] as! String
    print("File type \(type)")
} catch let error {
    print("Error: \(error.localizedDescription)")
}

When executed, results similar to the following output will appear in the Xcode console:

File type NSFileTypeDirectory

Summary

iOS provides options for both local and cloud-based file storage. Like most other operating systems, iOS supports storing and managing files on local devices using a file and directory-based filesystem structure. Each iOS app installed on a device is provided with a filesystem area within which files and directories may be stored and retrieved. This chapter has explored how directories are created, deleted, and navigated from within Swift code.

The next chapter will continue this theme by covering device-based file handling within iOS.

 

You are reading a sample chapter from Building iOS 17 Apps using Xcode Storyboards.

Buy the full book now in eBook or Print format.

The full book contains 96 chapters and 760 pages of in-depth information.

Learn more.

Preview  Buy eBook  Buy Print

 


Categories