Bryan Hanson Bryan Hanson - 5 months ago 21
iOS Question

Query Available iOS Disk Space with Swift

I'm trying to get the available iOS device storage using

Swift
. I found this function here

func deviceRemainingFreeSpaceInBytes() -> NSNumber {
let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil)
return systemAttributes[NSFileSystemFreeSize] as NSNumber
}


But at compile time this error is given:
[NSObject : AnyObject]? does not have a member named 'subscript'
I believe this error arises from the issue mentioned here, namely that
attributesOfFileSystemForPath
returns an optional dictionary (documentation). I understand the problem in a general sense, but because the suggested solution involves a nested case, I don't quite see how to fix the function I'm interested in (it doesn't help that I'm quite new to
Swift
). Can someone suggest how to make the function work? NOTE: I'm not sure if the original function was tested by the author or if it worked under an xcode 6 beta, but it doesn't work under the GM as far as I can see.

Answer

Optional binding with if let works here as well.

I would suggest that the function returns an optional Int64, so that it can return nil to signal a failure:

func deviceRemainingFreeSpaceInBytes() -> Int64? {
    let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    if let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil) {
        if let freeSize = systemAttributes[NSFileSystemFreeSize] as? NSNumber {
            return freeSize.longLongValue
        }
    }
    // something failed
    return nil
}

Swift 2.1 Update:

func deviceRemainingFreeSpaceInBytes() -> Int64? {
    let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    if let systemAttributes = try? NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last!) {
        if let freeSize = systemAttributes[NSFileSystemFreeSize] as? NSNumber {
            return freeSize.longLongValue
        }
    }
    // something failed
    return nil
}

Alternatively, you can use optional chaining. This is shorter, but not necessarily easier to read:

func deviceRemainingFreeSpaceInBytes() -> Int64? {
    let documentDirectoryPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let systemAttributes = NSFileManager.defaultManager().attributesOfFileSystemForPath(documentDirectoryPath.last as String, error: nil)
    let freeSize = (systemAttributes?[NSFileSystemFreeSize] as? NSNumber)?.longLongValue
    return freeSize
}

Usage:

if let bytes = deviceRemainingFreeSpaceInBytes() {
    println("free space: \(bytes)")
} else {
    println("failed")
}
Comments