J'essaie d'obtenir le chemin d'accès au dossier Documents avec le code:
var documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory:0,NSSearchPathDomainMask:0,true)
mais Xcode donne l'erreur: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'
J'essaie de comprendre ce qui ne va pas dans le code.
Apparemment, le compilateur pense que NSSearchPathDirectory:0
est un tableau et, bien sûr, il attend le type NSSearchPathDirectory
à la place. Ce n'est certainement pas un message d'erreur utile.
Mais quant aux raisons:
Tout d'abord, vous confondez les noms et les types d'arguments. Regardez la définition de la fonction:
func NSSearchPathForDirectoriesInDomains(
directory: NSSearchPathDirectory,
domainMask: NSSearchPathDomainMask,
expandTilde: Bool) -> AnyObject[]!
directory
et domainMask
sont les noms, vous utilisez les types, mais vous devez les laisser de toute façon pour les fonctions. Ils sont principalement utilisés dans les méthodes.Cela nous laisse donc (mis à jour pour Swift 2.0):
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
et pour Swift 3:
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
Swift 3.0 et 4.
Obtenir directement le premier élément d'un tableau peut potentiellement causer une exception si le chemin n'est pas trouvé. Donc, appeler first
puis décompresser est la meilleure solution
if let documentsPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first {
//This gives you the string formed path
}
if let documentsPathURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
//This gives you the URL of the path
}
La recommandation moderne consiste à utiliser des NSURL pour les fichiers et les répertoires au lieu des chemins basés sur NSString:
Donc, pour obtenir le répertoire de documents pour l'application en tant que NSURL:
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory: NSURL = urls.first as? NSURL {
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("items.db")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("items", withExtension: "db") {
let success = fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL, error: nil)
if success {
return finalDatabaseURL
} else {
println("Couldn't copy file to final location!")
}
} else {
println("Couldn't find initial database in the bundle!")
}
}
} else {
println("Couldn't get documents directory!")
}
return nil
}
La gestion des erreurs est rudimentaire, car cela dépend de ce que votre application fera dans de tels cas. Mais cela utilise des URL de fichier et une API plus moderne pour renvoyer l'URL de la base de données, en copiant la version initiale de l'ensemble si elle n'existe pas déjà, ou à zéro en cas d'erreur.
Xcode 8.2.1 • Swift 3.0.2
let documentDirectoryURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
Xcode 7.1.1 • Swift 2.1
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
D'habitude je préfère utiliser cette extension:
Swift 3.x et Swift 4.:
extension FileManager {
class func documentsDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) as [String]
return paths[0]
}
class func cachesDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) as [String]
return paths[0]
}
}
Swift 2.x:
extension NSFileManager {
class func documentsDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) as [String]
return paths[0]
}
class func cachesDir() -> String {
var paths = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true) as [String]
return paths[0]
}
}
Pour tous ceux qui recherchent un exemple fonctionnant avec Swift 2.2, le code d'Abizern avec le code moderne n'essaie pas de rattraper l'erreur d'erreur
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
if let documentDirectory:NSURL = urls.first { // No use of as? NSURL because let urls returns array of NSURL
// This is where the database should be in the documents directory
let finalDatabaseURL = documentDirectory.URLByAppendingPathComponent("OurFile.plist")
if finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) {
// The file already exists, so just return the URL
return finalDatabaseURL
} else {
// Copy the initial file from the application bundle to the documents directory
if let bundleURL = NSBundle.mainBundle().URLForResource("OurFile", withExtension: "plist") {
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch let error as NSError {// Handle the error
print("Couldn't copy file to final location! Error:\(error.localisedDescription)")
}
} else {
print("Couldn't find initial database in the bundle!")
}
}
} else {
print("Couldn't get documents directory!")
}
return nil
}
Mise à jour J'ai raté cette nouvelle Swift 2.0 garde (Ruby sauf si analogique), donc avec garde c'est beaucoup plus court et plus lisible
func databaseURL() -> NSURL? {
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
// If array of path is empty the document folder not found
guard urls.count != 0 else {
return nil
}
let finalDatabaseURL = urls.first!.URLByAppendingPathComponent("OurFile.plist")
// Check if file reachable, and if reacheble just return path
guard finalDatabaseURL.checkResourceIsReachableAndReturnError(nil) else {
// Check if file is exists in bundle folder
if let bundleURL = NSBundle.mainBundle().URLForResource("OurFile", withExtension: "plist") {
// if exist we will copy it
do {
try fileManager.copyItemAtURL(bundleURL, toURL: finalDatabaseURL)
} catch let error as NSError { // Handle the error
print("File copy failed! Error:\(error.localizedDescription)")
}
} else {
print("Our file not exist in bundle folder")
return nil
}
return finalDatabaseURL
}
return finalDatabaseURL
}
Plus pratique Swift méthode:
let documentsUrl = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
Xcode 8b4 Swift 3.
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
Habituellement, je préfère comme ci-dessous dans Swift, parce que je peux ajouter un nom de fichier et créer un fichier facilement
let fileManager = FileManager.default
if let documentsURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first {
let databasePath = documentsURL.appendingPathComponent("db.sqlite3").path
print("directory path:", documentsURL.path)
print("database path:", databasePath)
if !fileManager.fileExists(atPath: databasePath) {
fileManager.createFile(atPath: databasePath, contents: nil, attributes: nil)
}
}