Как найти NSDocumentDirectory в Swift?


162

Я пытаюсь получить путь к папке «Документы» с кодом:

var documentsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory:0,NSSearchPathDomainMask:0,true)

но Xcode выдает ошибку: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'

Я пытаюсь понять, что не так в коде.


1
В этот вопрос было внесено несколько изменений, которые добавляли возможные решения. Все это было беспорядок. Я откатился на первую версию для ясности. Ответы не принадлежат вопросу и должны быть опубликованы как ответы. Я готов обсудить, если кто-то считает мой откат слишком радикальным. Спасибо.
Эрик Ая

Ответы:


256

По-видимому, компилятор считает, что NSSearchPathDirectory:0это массив, и, конечно, он ожидает тип NSSearchPathDirectory. Конечно, не полезное сообщение об ошибке.

Но что касается причин:

Во-первых, вы путаете имена и типы аргументов. Взгляните на определение функции:

func NSSearchPathForDirectoriesInDomains(
    directory: NSSearchPathDirectory,
    domainMask: NSSearchPathDomainMask,
    expandTilde: Bool) -> AnyObject[]!
  • directoryи domainMaskэто имена, вы используете типы, но вы все равно должны оставить их для функций. Они используются в основном в методах.
  • Кроме того, Swift строго типизирован, поэтому вы не должны просто использовать 0. Вместо этого используйте значение enum.
  • И, наконец, он возвращает массив, а не только один путь.

Так что это оставляет нас (обновлено для Swift 2.0):

let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]

и для Swift 3:

let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]

Этот ответ не удалось в Xcode 6.0. В ролях должно быть, NSStringа не String.
Даниэль Т.

1
@DanielT. Просто попробовал еще раз и Stringработает в Xcode 6.0 и 6.1. Как правило, Stringи NSStringсоединяются автоматически в Swift. Может быть, вам пришлось бросить по NSStringдругой причине.
nschum

Вы пробовали это на детской площадке или в реальном приложении? Результаты разные. Код преобразуется Stringв игровую площадку, но не в приложение. Проверьте вопрос ( stackoverflow.com/questions/26450003/… )
Даниэль Т.

И то и другое. Полагаю, в Swift может быть небольшая ошибка ... Я просто отредактирую ответ, чтобы быть в безопасности. :) Спасибо
nschum

3
При повторном запуске приложение генерирует другой путь, почему ?: (1) /var/mobile/Containers/Data/Application/9E18A573-6429-434D-9B42-08642B643970/Documents(2)/var/mobile/Containers/Data/Application/77C8EA95-B77A-474D-8522-1F24F854A291/Documents
Янос

40

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
}

32

Современная рекомендация - использовать 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-адреса базы данных, копирования исходной версии из пакета, если она еще не существует, или нулевого значения в случае ошибки.


24

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)

21

Обычно я предпочитаю использовать это расширение:

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)")вы можете вызывать ее без различий между потоками (основной или фоновый).
Алессандро Орнано

14

Для всех, кто смотрит пример, который работает с Swift 2.2, код Abizern с современным постарайтесь поймать дескриптор ошибки

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 
}

13

Более удобный метод Swift 3 :

let documentsUrl = FileManager.default.urls(for: .documentDirectory, 
                                             in: .userDomainMask).first!

1
Или дажеFileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
Юлиан Онофрей

7
Я не думаю, что необходимо создавать новый файловый менеджер каждый раз, когда мы хотим получить URL для каталога Documents.
Андрей Гордеев

1
Я думал, что это то же самое. Спасибо!
Юлиан Онофрей

5

Xcode 8b4 Swift 3.0

let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)

3

Обычно я предпочитаю, как показано ниже в 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)
    }
}

1
absoluteStringнеправильно. чтобы получить путь к файлу для fileURL, вам нужно получить его .pathсвойство
Leo
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.