web-dev-qa-db-fra.com

Interroger l'espace disque disponible sur iOS avec Swift

J'essaie d'obtenir le stockage de périphérique iOS disponible à l'aide de Swift. J'ai trouvé cette fonction ici

        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
        }

Mais au moment de la compilation, cette erreur est donnée: [NSObject : AnyObject]? does not have a member named 'subscript' Je crois que cette erreur provient du problème mentionné ici , à savoir que attributesOfFileSystemForPath renvoie un dictionnaire optionnel ( documentation ). Je comprends le problème dans son sens général, mais comme la solution proposée implique un cas imbriqué, je ne vois pas trop comment résoudre le problème qui m’intéresse (cela ne aide en rien que je sois assez nouveau pour Swift) . Quelqu'un peut-il suggérer comment faire fonctionner la fonction? NOTE: Je ne suis pas sûr si la fonction originale a été testée par l'auteur ou si elle a fonctionné sous une version bêta de xcode 6, mais cela ne fonctionne pas sous le GM à ma connaissance.

18
Bryan Hanson

mise à jour iOS 11

Les réponses données ci-dessous ne fournissent plus de résultats précis sous iOS 11. De nouvelles clés de capacité de volume pouvant être passées à URL.resourceValues(forKeys:) fournissent des valeurs correspondant à celles disponibles dans les paramètres du périphérique.

  • static let volumeAvailableCapacityKey: URLResourceKey Clé de la capacité disponible du volume en octets (en lecture seule).

  • static let volumeAvailableCapacityForImportantUsageKey: URLResourceKey Clé de la capacité disponible du volume en octets pour le stockage des ressources importantes (en lecture seule).

  • static let volumeAvailableCapacityForOpportunisticUsageKey: URLResourceKey Clé de la capacité disponible du volume en octets pour le stockage des ressources non essentielles (en lecture seule).

  • static let volumeTotalCapacityKey: URLResourceKey Clé de la capacité totale du volume en octets (lecture seule).

De Documentation Apple :

Vue d'ensemble

Avant d'essayer de stocker localement une grande quantité de données, vérifiez d'abord que vous avez une capacité de stockage suffisante. Pour obtenir la capacité de stockage d'un volume, vous construisez une URL (à l'aide d'une instance d'URL) qui référence un objet sur le volume à interroger, puis interrogez ce volume.

Décider du type de requête à utiliser

Le type de requête à utiliser dépend de ce qui est stocké. Si vous stockez des données basées sur une requête utilisateur ou sur des ressources indispensables à l'application pour fonctionner correctement (par exemple, une vidéo que l'utilisateur est sur le point de regarder ou des ressources nécessaires pour le niveau suivant d'un jeu), lancez une requête sur volumeAvailableCapacityForImportantUsageKey. Toutefois, si vous téléchargez des données de manière plus prédictive (par exemple, le téléchargement d’un épisode d’une série télévisée récemment disponible que l’utilisateur a regardé récemment), effectuez une requête sur volumeAvailableCapacityForOpportunisticUsageKey.

Construire une requête

Utilisez cet exemple comme guide pour construire votre propre requête:

let fileURL = URL(fileURLWithPath: NSHomeDirectory() as String)
do {
    let values = try fileURL.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
    if let capacity = values.volumeAvailableCapacityForImportantUsage {
        print("Available capacity for important usage: \(capacity)")
    } else {
        print("Capacity is unavailable")
    }
} catch {
    print("Error retrieving capacity: \(error.localizedDescription)")
}

Réponse originale

Liaison facultative avec if let fonctionne également ici.

Je suggérerais que la fonction retourne un Int64 optionnel, de sorte qu'elle puisse renvoyer nil pour signaler un échec:

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
}

Mise à jour Swift 2.1:

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

Mise à jour Swift 3.0:

func deviceRemainingFreeSpaceInBytes() -> Int64? {
    let documentDirectory = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).last!
    guard
        let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: documentDirectory),
        let freeSize = systemAttributes[.systemFreeSize] as? NSNumber
    else {
        // something failed
        return nil
    }
    return freeSize.int64Value
}

Usage:

if let bytes = deviceRemainingFreeSpaceInBytes() {
    print("free space: \(bytes)")
} else {
    print("failed")
}
40
Martin R

J'ai écrit un cours pour obtenir de la mémoire disponible/utilisée avec Swift. Démo sur: https://github.com/thanhcuong1990/Swift-disk-status

Mise à niveau pour prendre en charge Swift 3.

import UIKit

class DiskStatus {

    //MARK: Formatter MB only
    class func MBFormatter(_ bytes: Int64) -> String {
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = ByteCountFormatter.Units.useMB
        formatter.countStyle = ByteCountFormatter.CountStyle.decimal
        formatter.includesUnit = false
        return formatter.string(fromByteCount: bytes) as String
    }


    //MARK: Get String Value
    class var totalDiskSpace:String {
        get {
            return ByteCountFormatter.string(fromByteCount: totalDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        }
    }

    class var freeDiskSpace:String {
        get {
            return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        }
    }

    class var usedDiskSpace:String {
        get {
            return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.binary)
        }
    }


    //MARK: Get raw value
    class var totalDiskSpaceInBytes:Int64 {
        get {
            do {
                let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
                let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value
                return space!
            } catch {
                return 0
            }
        }
    }

    class var freeDiskSpaceInBytes:Int64 {
        get {
            do {
                let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String)
                let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value
                return freeSpace!
            } catch {
                return 0
            }
        }
    }

    class var usedDiskSpaceInBytes:Int64 {
        get {
            let usedSpace = totalDiskSpaceInBytes - freeDiskSpaceInBytes
            return usedSpace
        }
    }

}

Démo: 

get disk space status with Swift

19
Cuong Lam

Eh bien, selon les codes ci-dessus:

let usedSpace = totalDiskSpaceInBytes - freeDiskSpaceInBytes

vous découvrirez peut-être que usedSpace ne correspond pas à la valeur de la page de configuration de l'iPhone. En effet, dans iOS11, Apple introduit Capacité totale disponible en octets pour les ressources "Important" .

Capacité totale disponible en octets pour les ressources "Important", y compris Espaces devant être libérés en purgeant les ressources non essentielles et mises en cache . "Important" signifie quelque chose que l'utilisateur ou l'application S'attend clairement à voir être présent sur le système local, mais qu'il est finalement Remplaçable. Cela comprend les éléments que A explicitement demandés à l'utilisateur via l'interface utilisateur, ainsi que les ressources nécessaires à une application afin de fournir des fonctionnalités. 

Exemples: Une vidéo que l'utilisateur A explicitement demandé à visionner mais n'a pas encore fini de regarder ou Un fichier audio que l'utilisateur a demandé de télécharger.

Cette valeur Ne doit pas être utilisée pour déterminer s’il reste de la place pour une ressource Irremplaçable. Dans le cas de ressources irremplaçables, tentez toujours de sauvegarder la ressource, quelle que soit la capacité disponible, et Gérerez les pannes de la manière la plus élégante possible.

Pour obtenir exactement la même valeur que ce que nous voyons dans la page de configuration de l'iPhone, nous pouvons obtenir de l'espace libre avec volumeAvailableCapacityForImportantUsage

if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage {
    return space ?? 0
}

Vous pouvez utiliser l’extension UIDevice suivante :

Swift4

extension UIDevice {
    func MBFormatter(_ bytes: Int64) -> String {
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = ByteCountFormatter.Units.useMB
        formatter.countStyle = ByteCountFormatter.CountStyle.decimal
        formatter.includesUnit = false
        return formatter.string(fromByteCount: bytes) as String
    }

    //MARK: Get String Value
    var totalDiskSpaceInGB:String {
       return ByteCountFormatter.string(fromByteCount: totalDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
    }

    var freeDiskSpaceInGB:String {
        return ByteCountFormatter.string(fromByteCount: freeDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
    }

    var usedDiskSpaceInGB:String {
        return ByteCountFormatter.string(fromByteCount: usedDiskSpaceInBytes, countStyle: ByteCountFormatter.CountStyle.decimal)
    }

    var totalDiskSpaceInMB:String {
        return MBFormatter(totalDiskSpaceInBytes)
    }

    var freeDiskSpaceInMB:String {
        return MBFormatter(freeDiskSpaceInBytes)
    }

    var usedDiskSpaceInMB:String {
        return MBFormatter(usedDiskSpaceInBytes)
    }

    //MARK: Get raw value
    var totalDiskSpaceInBytes:Int64 {
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let space = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value else { return 0 }
        return space
    }

    /*
     Total available capacity in bytes for "Important" resources, including space expected to be cleared by purging non-essential and cached resources. "Important" means something that the user or application clearly expects to be present on the local system, but is ultimately replaceable. This would include items that the user has explicitly requested via the UI, and resources that an application requires in order to provide functionality.
     Examples: A video that the user has explicitly requested to watch but has not yet finished watching or an audio file that the user has requested to download.
     This value should not be used in determining if there is room for an irreplaceable resource. In the case of irreplaceable resources, always attempt to save the resource regardless of available capacity and handle failure as gracefully as possible.
     */
    var freeDiskSpaceInBytes:Int64 {
        if #available(iOS 11.0, *) {
            if let space = try? URL(fileURLWithPath: NSHomeDirectory() as String).resourceValues(forKeys: [URLResourceKey.volumeAvailableCapacityForImportantUsageKey]).volumeAvailableCapacityForImportantUsage {
                return space ?? 0
            } else {
                return 0
            }
        } else {
            if let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value {
                return freeSpace
            } else {
                return 0
            }
        }
    }

    var usedDiskSpaceInBytes:Int64 {
       return totalDiskSpaceInBytes - freeDiskSpaceInBytes
    }

}

usage:

Logger.d("totalDiskSpaceInBytes: \(UIDevice.current.totalDiskSpaceInBytes)")
Logger.d("freeDiskSpace: \(UIDevice.current.freeDiskSpaceInBytes)")
Logger.d("usedDiskSpace: \(UIDevice.current.usedDiskSpaceInBytes)")

 enter image description here

19
User9527

Ceci est similaire à la réponse de Martin pour Swift 3.1 , mais est converti en une extension de UIDevice pour en faciliter l'accès.

extension UIDevice {
    var systemSize: Int64? {
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let totalSize = (systemAttributes[.systemSize] as? NSNumber)?.int64Value else {
                return nil
        }

        return totalSize
    }

    var systemFreeSize: Int64? {
        guard let systemAttributes = try? FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory() as String),
            let freeSize = (systemAttributes[.systemFreeSize] as? NSNumber)?.int64Value else {
                return nil
        }

        return freeSize
    }
}

Pour obtenir de l'espace libre:

UIDevice.current.systemFreeSize

Et pour obtenir un espace total:

UIDevice.current.systemSize
2
CodeBender