How To Get Directory Size With Swift On OS X

Swift 3 version here:

 func findSize(path: String) throws -> UInt64 {

    let fullPath = (path as NSString).expandingTildeInPath
    let fileAttributes: NSDictionary = try FileManager.default.attributesOfItem(atPath: fullPath) as NSDictionary

    if fileAttributes.fileType() == "NSFileTypeRegular" {
        return fileAttributes.fileSize()
    }

    let url = NSURL(fileURLWithPath: fullPath)
    guard let directoryEnumerator = FileManager.default.enumerator(at: url as URL, includingPropertiesForKeys: [URLResourceKey.fileSizeKey], options: [.skipsHiddenFiles], errorHandler: nil) else { throw FileErrors.BadEnumeration }

    var total: UInt64 = 0

    for (index, object) in directoryEnumerator.enumerated() {
        guard let fileURL = object as? NSURL else { throw FileErrors.BadResource }
        var fileSizeResource: AnyObject?
        try fileURL.getResourceValue(&fileSizeResource, forKey: URLResourceKey.fileSizeKey)
        guard let fileSize = fileSizeResource as? NSNumber else { continue }
        total += fileSize.uint64Value
        if index % 1000 == 0 {
            print(".", terminator: "")
        }
    }

    if total < 1048576 {
        total = 1
    }
    else
    {
        total = UInt64(total / 1048576)
    }

    return total
}

enum FileErrors : ErrorType {
    case BadEnumeration
    case BadResource
}

Output value is megabyte. Converted from source: https://gist.github.com/rayfix/66b0a822648c87326645


To anyone who is looking for a solution for Swift 5+ and Xcode 11+ look at this gist

func directorySize(url: URL) -> Int64 {
    let contents: [URL]
    do {
        contents = try FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: [.fileSizeKey, .isDirectoryKey])
    } catch {
        return 0
    }

    var size: Int64 = 0

    for url in contents {
        let isDirectoryResourceValue: URLResourceValues
        do {
            isDirectoryResourceValue = try url.resourceValues(forKeys: [.isDirectoryKey])
        } catch {
            continue
        }

        if isDirectoryResourceValue.isDirectory == true {
            size += directorySize(url: url)
        } else {
            let fileSizeResourceValue: URLResourceValues
            do {
                fileSizeResourceValue = try url.resourceValues(forKeys: [.fileSizeKey])
            } catch {
                continue
            }

            size += Int64(fileSizeResourceValue.fileSize ?? 0)
        }
    }
    return size

}


update: Xcode 11.4.1 • Swift 5.2


extension URL {
    /// check if the URL is a directory and if it is reachable 
    func isDirectoryAndReachable() throws -> Bool {
        guard try resourceValues(forKeys: [.isDirectoryKey]).isDirectory == true else {
            return false
        }
        return try checkResourceIsReachable()
    }

    /// returns total allocated size of a the directory including its subFolders or not
    func directoryTotalAllocatedSize(includingSubfolders: Bool = false) throws -> Int? {
        guard try isDirectoryAndReachable() else { return nil }
        if includingSubfolders {
            guard
                let urls = FileManager.default.enumerator(at: self, includingPropertiesForKeys: nil)?.allObjects as? [URL] else { return nil }
            return try urls.lazy.reduce(0) {
                    (try $1.resourceValues(forKeys: [.totalFileAllocatedSizeKey]).totalFileAllocatedSize ?? 0) + $0
            }
        }
        return try FileManager.default.contentsOfDirectory(at: self, includingPropertiesForKeys: nil).lazy.reduce(0) {
                 (try $1.resourceValues(forKeys: [.totalFileAllocatedSizeKey])
                    .totalFileAllocatedSize ?? 0) + $0
        }
    }

    /// returns the directory total size on disk
    func sizeOnDisk() throws -> String? {
        guard let size = try directoryTotalAllocatedSize(includingSubfolders: true) else { return nil }
        URL.byteCountFormatter.countStyle = .file
        guard let byteCount = URL.byteCountFormatter.string(for: size) else { return nil}
        return byteCount + " on disk"
    }
    private static let byteCountFormatter = ByteCountFormatter()
}

usage:

do {
    let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
    if let sizeOnDisk = try documentDirectory.sizeOnDisk() {
        print("Size:", sizeOnDisk) // Size: 3.15 GB on disk
    }
} catch {
    print(error)
}