Working with Files in Swift on iOS 10

From Techotopia
Jump to: navigation, search

PreviousTable of ContentsNext
Working with Directories in Swift on iOS 10iOS 10 Directory Handling and File I/O in Swift – A Worked Example


You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book


In the chapter entitled Working with Directories in Swift on iOS 10 we looked at the FileManager, FileHandle and Data Foundation Framework classes and discussed how the FileManager class in particular enables us to work with directories when developing iOS based applications. We also spent some time covering the file system structure used by iOS and, in particular, looked at the temporary and Documents directories assigned to each application and how the location of those directories can be identified from within the application code.

In this chapter we move on from working with directories to covering the details of working with files within the iOS 10 SDK. Once we have covered file handling topics in this chapter, the next chapter will work through an application example that puts theory into practice.




Obtaining a FileManager Instance Reference

Before proceeding, first we need to recap the steps necessary to obtain a reference to the application’s FileManager instance. As discussed in the previous chapter, the FileManager class contains a property named default that is used to obtain a reference. For example:

let filemgr = FileManager.default

Once a reference to the file manager object has been obtained it can be used to perform some basic file handling tasks.

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

Checking for the Existence of a File

The FileManager class contains an instance method named fileExists(atPath:) which checks whether a specified file already exists. The method takes as an argument an NSString object containing the path to the file in question and returns a Boolean value indicating the presence or otherwise of the specified file:

let filemgr = FileManager.default

if filemgr.fileExists(atPath: "/Applications") {
    print("File exists")
} else {
    print("File not found")
}

Comparing the Contents of Two Files

The contents of two files may be compared for equality using the contentsEqualAtPath method. This method takes as arguments the paths to the two files to be compared and returns a Boolean result to indicate whether the file contents match:

if filemgr.contentsEqual(atPath: filePath1, andPath: filePath2) {
    print("File contents match")
} else {
    print("File contents do not match")
}

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

Checking if a File is Readable/Writable/Executable/Deletable

Most operating systems provide some level of file access control. These typically take the form of attributes designed to control the level of access to a file for each user or user group. As such, it is not a certainty that your program will have read or write access to a particular file, or the appropriate permissions to delete or rename it. The quickest way to find out if your program has a particular access permission is to use the isReadableFile(atPath:), isWritableFile(atPath:), isExecutableFile(atPath:) and isDeletableFile(atPath:) methods. Each method takes a single argument in the form of the path to the file to be checked and returns a Boolean result. For example, the following code excerpt checks to find out if a file is writable:

if filemgr.isWritableFile(atPath: filepath1) {
    print("File is writable")
} else {
    print("File is read-only")
}

To check for other access permissions simply substitute the corresponding method name in place of isWritableFile(atPath:) in the above example.

Moving/Renaming a File

A file may be renamed (assuming adequate permissions) using the moveItem(atPath:) method. This method takes as arguments the pathname for the file to be moved and the destination path. Note that if the destination file path already exists this operation will fail.

do {
    try filemgr.moveItem(atPath: filepath1, toPath: filepath2)
    print("Move successful")
} catch let error {
    print("Error: \(error.localizedDescription)")
}

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

Copying a File

File copying can be achieved using the copyItem(atPath:) method. As with the move method, this takes as arguments the source and destination pathnames:

do {
    try filemgr.copyItem(atPath: filepath1, toPath: filepath2)
    print("Copy successful")
} catch let error {
    print("Error: \(error.localizedDescription)")
}

Removing a File

The removeItem(atPath:) method removes the specified file from the file system. The method takes as an argument the pathname of the file to be removed:

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

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

Creating a Symbolic Link

A symbolic link to a particular file may be created using the createSymbolicLink(atPath:) method. This takes as arguments the path of the symbolic link and the path to the file to which the link is to refer:

do {
    try filemgr.createSymbolicLink(atPath: filepath2, 
            withDestinationPath: filepath1)
    print("Link successful")
} catch let error {
    print("Error: \(error.localizedDescription)")
}

Reading and Writing Files with FileManager

The FileManager class includes some basic file reading and writing capabilities. These capabilities are somewhat limited when compared to the options provided by the FileHandle class, but can be useful nonetheless.

First, the contents of a file may be read and stored in a Data object through the use of the contents(atPath:) method:

let databuffer = filemgr.contents(atPath: filepath1)

Having stored the contents of a file in a Data object that data may subsequently be written out to a new file using the createFile(atPath:) method:

filemgr.createFile(atPath: filepath2, contents: databuffer, 
				attributes: nil)

In the above example we have essentially copied the contents from an existing file to a new file. This, however, gives us no control over how much data is to be read or written and does not allow us to append data to the end of an existing file. If the file in the above example had already existed it, and any data it contained, would have been overwritten by the contents of the source file. Clearly some more flexible mechanism is required. This is provided by the Foundation Framework in the form of the FileHandle class.

Working with Files using the FileHandle Class

The FileHandle class provides a range of methods designed to provide a more advanced mechanism for working with files. In addition to files, this class can also be used for working with devices and network sockets. In the following sections we will look at some of the more common uses for this class.

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

Creating a FileHandle Object

A FileHandle object can be created when opening a file for reading, writing or updating (in other words both reading and writing). Having opened a file, it must subsequently be closed when we have finished working with it using the closeFile method. If an attempt to open a file fails, for example because an attempt is made to open a non-existent file for reading, these methods return nil.

For example, the following code excerpt opens a file for reading and then closes it without actually doing anything to the file:

let file: FileHandle? = FileHandle(forReadingAtPath: filepath1)

    if file == nil {
        print("File open failed")
    } else {
        file?.closeFile()
}

FileHandle File Offsets and Seeking

FileHandle objects maintain a pointer to the current position in a file. This is referred to as the offset. When a file is first opened the offset is set to 0 (the beginning of the file). This means that any read or write operations performed using the FileHandle instance methods will take place at offset 0 in the file. To perform operations at different locations in a file (for example to append data to the end of the file) it is first necessary to seek to the required offset. For example to move the current offset to the end of the file, use the seekToEndOfFile method. Alternatively, seek(toFileOffset:) allows you to specify the precise location in the file to which the offset is to be positioned. Finally, the current offset may be identified using the offsetInFile method. In order to accommodate large files, the offset is stored in the form of an unsigned 64-bit integer.

The following example opens a file for reading and then performs a number of method calls to move the offset to different positions, outputting the current offset after each move:

let file: FileHandle? = FileHandle(forReadingAtPath: filepath1)

if file == nil {
    print("File open failed")
} else {
    print("Offset = \(file?.offsetInFile)")
    file?.seekToEndOfFile()
    print("Offset = \(file?.offsetInFile)")
    file?.seek(toFileOffset: 30)
    print("Offset = \(file?.offsetInFile)")
    file?.closeFile()
}

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

File offsets are a key aspect of working with files using the FileHandle class so it is worth taking extra time to make sure you understand the concept. Without knowing where the current offset is in a file it is impossible to know the location in the file where data will be read or written.

Reading Data from a File

Once a file has been opened and assigned a file handle, the contents of that file may be read from the current offset position. The readData(ofLength:) method reads a specified number of bytes of data from the file starting at the current offset. For example, the following code reads 5 bytes of data from offset 10 in a file. The data read is returned encapsulated in a Data object:

let file: FileHandle? = FileHandle(forReadingAtPath: filepath1)

if file == nil {
    print("File open failed")
} else {
    file?.seek(toFileOffset: 10)
    let databuffer = file?.readData(ofLength: 5)
    file?.closeFile()
}

Alternatively, the readDataToEndOfFile method will read all the data in the file starting at the current offset and ending at the end of the file.

Writing Data to a File

The write method writes the data contained in a Data object to the file starting at the location of the offset. Note that this does not insert data but rather overwrites any existing data in the file at the corresponding location.

To see this in action, let’s assume the existence of a file named quickfox.txt containing the following text:

The quick brown fox jumped over the lazy dog

Next, we will write code that opens the file for updating, seeks to position 10 and then writes some data at that location:

let file: FileHandle? = FileHandle(forUpdatingAtPath: filepath1)

if file == nil {
    print("File open failed")
} else {
    let data = ("black dog" as
                    NSString).data(using: String.Encoding.utf8.rawValue)
    file?.seek(toFileOffset: 10)
    file?.write(data!)
    file?.closeFile()
}

You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book

When the above program is compiled and executed the contents of the quickfox.txt file will have changed to:

The quick black dog jumped over the lazy dog

Truncating a File

A file may be truncated at the specified offset using the truncateFile(atOffset:) method. To delete the entire contents of a file, specify an offset of 0 when calling this method:

let file: FileHandle? = FileHandle(forUpdatingAtPath: filepath1)

if file == nil {
    print("File open failed")
} else {
    file?.truncateFile(atOffset: 0)
    file?.closeFile()
}

Summary

Much like other operating systems, iOS provides a file system for the purposes of locally storing user and application files and data. In this and the preceding chapter, details of file and directory handling have been covered in some detail. The next chapter, entitled iOS 10 Directory Handling and File I/O in Swift – A Worked Example will work through the creation of an example designed specifically to demonstrate iOS file and directory handling.


You are reading a sample chapter from the iOS 10 App Development Essentials book.

Purchase the full iOS 10 / Swift 3 / Xcode 8 edition of this book in eBook ($19.99) or Print ($45.99) format.
iOS 10 App Development Essentials Print and eBook (ePub/PDF/Kindle) edition contains over 100 chapters. Learn more...

Buy eBook Buy Print Preview Book


PreviousTable of ContentsNext
Working with Directories in Swift on iOS 8iOS 10 Directory Handling and File I/O in Swift – A Worked Example