Bagaimana menemukan NSDocumentDirectory di Swift?

162

Saya mencoba mendapatkan jalur ke folder Dokumen dengan kode:

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

tetapi Xcode memberikan kesalahan: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'

Saya mencoba memahami apa yang salah dalam kode.

Ivan R
sumber
1
Ada beberapa suntingan untuk pertanyaan ini yang menambahkan kemungkinan solusi. Semuanya berantakan. Saya telah memutar kembali ke versi pertama untuk kejelasan. Jawaban tidak termasuk dalam pertanyaan dan harus diposting sebagai jawaban. Saya siap berdiskusi jika seseorang berpikir bahwa kembalikan saya terlalu radikal. Terima kasih.
Eric Aya

Jawaban:

254

Rupanya, kompiler berpikir NSSearchPathDirectory:0adalah sebuah array, dan tentu saja mengharapkan tipe itu NSSearchPathDirectorysebagai gantinya. Jelas bukan pesan kesalahan yang membantu.

Tetapi untuk alasan:

Pertama, Anda mengacaukan nama dan tipe argumen. Lihatlah definisi fungsi:

func NSSearchPathForDirectoriesInDomains(
    directory: NSSearchPathDirectory,
    domainMask: NSSearchPathDomainMask,
    expandTilde: Bool) -> AnyObject[]!
  • directorydan domainMaskapakah namanya, Anda menggunakan tipenya, tetapi Anda tetap harus membiarkannya untuk fungsi. Mereka digunakan terutama dalam metode.
  • Juga, Swift sangat diketik, jadi Anda tidak harus hanya menggunakan 0. Gunakan nilai enum sebagai gantinya.
  • Dan akhirnya, ia mengembalikan sebuah array, bukan hanya satu jalur.

Sehingga meninggalkan kita dengan (diperbarui untuk Swift 2.0):

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

dan untuk Swift 3:

let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
nschum
sumber
Jawaban ini gagal dalam Xcode 6.0. Para pemain harus untuk NSStringdaripada String.
Daniel T.
1
@DanielT. Baru saja mencoba lagi dan Stringberfungsi di Xcode 6.0 dan 6.1. Secara umum, Stringdan NSStringdijembatani secara otomatis di Swift. Mungkin Anda harus ikut NSStringkarena alasan lain.
nschum
Apakah Anda mencobanya di taman bermain atau di aplikasi yang sebenarnya? Hasilnya berbeda. Kode dilemparkan ke dalam Stringdi taman bermain, tetapi tidak dalam aplikasi. Lihat pertanyaan ( stackoverflow.com/questions/26450003/... )
Daniel T.
Keduanya sebenarnya. Bisa jadi bug halus di Swift, saya kira ... Saya hanya akan mengedit jawaban agar aman. :) Terima kasih
nschum
3
berjalan lagi aplikasi menghasilkan jalur yang berbeda, mengapa ?: (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 dan 4.0

Langsung mendapatkan elemen pertama dari array berpotensi menyebabkan pengecualian jika jalur tidak ditemukan. Jadi menelepon firstdan membuka bungkusan adalah solusi yang lebih baik

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
}
Fangming
sumber
32

Rekomendasi modern adalah untuk menggunakan NSURLs untuk file dan direktori, bukan jalur berbasis NSString:

masukkan deskripsi gambar di sini

Jadi untuk mendapatkan direktori Dokumen untuk aplikasi sebagai 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
}

Ini memiliki penanganan kesalahan yang belum sempurna, karena hal itu tergantung pada apa yang akan dilakukan aplikasi Anda dalam kasus tersebut. Tapi ini menggunakan URL file dan api yang lebih modern untuk mengembalikan URL database, menyalin versi awal dari bundel jika belum ada, atau nol jika terjadi kesalahan.

Abizern
sumber
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)
Leo Dabus
sumber
21

Biasanya saya lebih suka menggunakan ekstensi ini:

Swift 3.x dan 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]
    }
}
Alessandro Ornano
sumber
di mana memanggil ini?
B.Saravana Kumar
Untuk setiap bagian dari kode yang Anda butuhkan:, let path = FileManager.documentsDir()+("/"+"\(fileName)")Anda dapat menyebutnya tanpa perbedaan antara utas (utama atau latar belakang).
Alessandro Ornano
14

Untuk semua orang yang melihat contoh yang bekerja dengan Swift 2.2, kode Abizern dengan modern jangan coba-coba menangani kesalahan

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
}

Pembaruan Saya telah melewatkan bahwa swift 2.0 baru memiliki pelindung (Ruby kecuali analog), jadi dengan pelindung itu jauh lebih pendek dan lebih mudah dibaca

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 
}
Roman Safin
sumber
13

Metode Swift 3 yang lebih nyaman :

let documentsUrl = FileManager.default.urls(for: .documentDirectory, 
                                             in: .userDomainMask).first!
Andrey Gordeev
sumber
1
Atau bahkanFileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
Iulian Onofrei
7
Saya pikir tidak perlu membuat instantiate file manager baru setiap kali kami ingin mendapatkan URL untuk direktori Documents.
Andrey Gordeev
1
Saya pikir itu hal yang sama. Terima kasih!
Iulian Onofrei
5

Xcode 8b4 Swift 3.0

let paths = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.documentDirectory, FileManager.SearchPathDomainMask.userDomainMask, true)
Siavash Alp
sumber
3

Biasanya saya lebih suka seperti di bawah ini di swift 3 , karena saya dapat menambahkan nama file dan membuat file dengan mudah

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)
    }
}
lakukan01
sumber
1
absoluteStringsalah. untuk mendapatkan path file dari fileURL Anda perlu mendapatkan .pathpropertinya
Leo Dabus