Я пытаюсь получить путь к папке Documents с кодом:
var documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory:0,NSSearchPathDomainMask:0,true)
но xcode дает ошибку: невозможно преобразовать тип выражения "AnyObject []! ' для ввода" NSSearchPathDirectory"
Я пытаюсь понять, что не так в коде?
По-видимому, компилятор считает, что NSSearchPathDirectory:0
- это массив, и, конечно, он ожидает тип NSSearchPathDirectory
. Конечно, это не сообщение об ошибке.
Но что касается причин:
Во-первых, вы путаете имена и типы аргументов. Взгляните на определение функции:
func NSSearchPathForDirectoriesInDomains(
directory: NSSearchPathDirectory,
domainMask: NSSearchPathDomainMask,
expandTilde: Bool) -> AnyObject[]!
directory
и domainMask
- это имена, вы используете типы, но в любом случае вы должны оставить их для функций. Они используются главным образом в методах.Итак, это оставляет нас (обновлено для Swift 2.0):
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
и для Swift 3:
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
NSString
а не к String
.
String
работает в Xcode 6.0 и 6.1. Обычно String
и NSString
автоматически соединяются в Swift. Может быть, вам пришлось бросить на NSString
по другой причине.
Современная рекомендация - использовать NSURL для файлов и каталогов вместо путей на основе NSString:
Итак, чтобы получить каталог Document для приложения в виде 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
}
Это рудиментарная обработка ошибок, так как это зависит от того, что ваше приложение будет делать в таких случаях. Но это использует URL-адреса файлов и более современный api, чтобы вернуть URL-адрес базы данных, скопировав исходную версию из пакета, если он еще не существует, или нуль в случае ошибки.
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)
Для всех, кто смотрит пример, который работает с Swift 2.2, код Abizern с современными do try catch handle ошибки
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
}
Обновление Я пропустил этот новый swift 2.0 с защитой (Ruby, если не аналоговый), поэтому с защитой он намного короче и читабельнее
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
}
Обычно я предпочитаю использовать это расширение:
Swift 3.x и Swift 4.0:
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]
}
}
let path = FileManager.documentsDir()+("/"+"\(fileName)")
, вы можете вызывать его без различий между потоками (основной или фоновый).
Более удобный метод Swift 3:
let documentsUrl = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
FileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
Swift 3.0 и 4.0
Непосредственное получение первого элемента из массива потенциально может вызвать исключение, если путь не найден. Поэтому вызов first
, а затем разворот - лучшее решение
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
}
Xcode 8b4 Swift 3.0
let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
Обычно я предпочитаю, как показано ниже в swift 3, потому что я могу добавить имя файла и легко создать файл
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)
}
}
absoluteString
неверно. чтобы получить путь к файлу для fileURL, вам нужно получить его свойство .path