Як знайти 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/… )
Daniel T.

Обидва, власне. Можливо, може бути непомітна помилка у Свіфті, я думаю ... я просто відредагую відповідь, щоб бути безпечним. :) Дякую
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
János

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-шляхів:

введіть тут опис зображення

Отже, щоб отримати каталог документів для програми як 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 • Швидкий 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]
    }
}

Швидкий 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
}

Оновлення Я пропустив, що новий швидкий 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-адресу для каталогу документів.
Андрій Гордєєв

1
Я думав, що це те саме. Дякую!
Юліан Онофрей

5

Xcode 8b4 Swift 3.0

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

3

Зазвичай я вважаю за краще, як нижче, у швидкому 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неправильно. щоб отримати шлях до файлуURL, вам потрібно отримати його .pathмайно
Лев Дабус
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.