How to get file size using FileManager + formatting

Turns out getting the byte size is quite quick. This post also shows how to use ByteCountFormatter to transform byte count into human-readable string.

If you need to get file size programmatically in Swift using the file's URL then it is just a matter of a few lines of code. Let's see how. All we need is an URL for the file in question.

Getting file size with FileManager

We are going to use method attributesOfItem available on the FileManager. As the naming suggests this will return attributes for item. In our case a file. We need to supply a path to the file which we can get from the URL.

if let fileAttributes = try? FileManager.default.attributesOfItem(atPath: fileURL.path) {
}

This method can throw so we are using try? to safely get the attributes or nil in case of an issue. In this case that would most likely be incorrect path or for some reason unreadable file.

fileAttributes is a dictionary and we can get size like so:

if let bytes = fileAttributes[.size] as? Int64 {
}

The whole thing is in bytes meaning it is not very human-readable number.

Extension

We can also create handy extension to abstract away all this code. Feel free to use it in your project 🙂

extension FileManager {
    func sizeOfFile(atPath path: String) -> Int64? {
            guard let attrs = try? attributesOfItem(atPath: path) else {
                return nil
            }

            return attrs[.size] as? Int64
    }
}

It depends whether you need to use file size internally (to maybe estimate upload time etc) or if you want to present the result to the user.

In the latter case do not display number of bytes but instead convert it to readable number.

Presenting file size to the user with ByteCountFormatter

Swift has really cool and useful class called ByteCountFormatter which can format byte count to readable string.

Let's use it to format our file size.

ByteCountFormatter usage

let bcf = ByteCountFormatter()
bcf.allowedUnits = [.useMB]
bcf.countStyle = .file
let string = bcf.string(fromByteCount: bytes)

We first create instance, then set allowedUnits in this case to only megabytes because we are expecting files bigger than 1 MB and smaller than 1 GB and set countStyle to .file.

Then it is just a matter of getting the resulting string. ByteCountFormatter will automatically append unit (in this case "MB") so we don't have to do any post-formatting. 👏

The result will be something like "24,4 MB" with either "," or "." as a separator depending on the user's locale settings.

For frequent usage I would recommend creating something like a static instance to avoid creating instances over and over again and have centralized configuration.

private let fileByteCountFormatter: ByteCountFormatter = {
        let bcf = ByteCountFormatter()
        bcf.allowedUnits = [.useMB]
        bcf.countStyle = .file
        return bcf
}()

And that is it!

Thanks for reading!

Uses: Xcode 11 & Swift 5