Làm cách nào để tìm NSDocumentDirectory trong Swift?


162

Tôi đang cố gắng lấy đường dẫn đến thư mục Documents bằng mã:

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

nhưng Xcode báo lỗi: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'

Tôi đang cố gắng hiểu những gì sai trong mã.


1
Có một số chỉnh sửa cho câu hỏi này đã thêm các giải pháp có thể. Toàn bộ sự việc là một mớ hỗn độn. Tôi đã quay trở lại phiên bản đầu tiên cho rõ ràng. Câu trả lời không thuộc về một câu hỏi và nên được đăng dưới dạng câu trả lời. Tôi sẵn sàng thảo luận nếu ai đó nghĩ rằng việc khôi phục của tôi quá triệt để. Cảm ơn.
Eric Aya

Câu trả lời:


254

Rõ ràng, trình biên dịch nghĩ NSSearchPathDirectory:0là một mảng, và tất nhiên nó mong đợi kiểu NSSearchPathDirectorythay thế. Chắc chắn không phải là một thông báo lỗi hữu ích.

Nhưng như lý do:

Đầu tiên, bạn đang nhầm lẫn giữa các tên và kiểu đối số. Hãy xem định nghĩa hàm:

func NSSearchPathForDirectoriesInDomains(
    directory: NSSearchPathDirectory,
    domainMask: NSSearchPathDomainMask,
    expandTilde: Bool) -> AnyObject[]!
  • directorydomainMasklà tên, bạn đang sử dụng các loại, nhưng dù sao bạn cũng nên để chúng ra cho các chức năng. Chúng được sử dụng chủ yếu trong các phương pháp.
  • Ngoài ra, Swift được gõ mạnh, vì vậy bạn không nên sử dụng 0. Thay vào đó hãy sử dụng giá trị của enum.
  • Và cuối cùng, nó trả về một mảng, không chỉ là một đường dẫn.

Vì vậy, điều đó cho chúng tôi (cập nhật cho Swift 2.0):

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

và cho Swift 3:

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

Câu trả lời này không thành công trong Xcode 6.0. Các diễn viên phải được NSStringchứ không phải String.
Daniel T.

1
@DanielT. Chỉ cần thử lại và Stringhoạt động trong Xcode 6.0 và 6.1. Nói chung, StringNSStringđược bắc cầu tự động trong Swift. Có lẽ bạn phải bỏ qua NSStringvì một lý do khác.
nschum

Bạn đã thử nó trong một sân chơi hoặc trong một ứng dụng thực tế? Kết quả là khác nhau. Mã này chuyển đến một Stringtrong một sân chơi, nhưng không phải trong một ứng dụng. Kiểm tra câu hỏi ( stackoverflow.com/questions/26450003/
Daniel T.

Cả hai, thực sự. Có thể là một lỗi tinh vi trong Swift, tôi cho rằng ... Tôi sẽ chỉ chỉnh sửa câu trả lời để an toàn. :) Cảm ơn
nschum

3
chạy lại ứng dụng sẽ tạo ra một đường dẫn khác, tại sao?: (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 và 4.0

Trực tiếp nhận phần tử đầu tiên từ một mảng sẽ có khả năng gây ra ngoại lệ nếu không tìm thấy đường dẫn. Vì vậy, gọi firstvà sau đó unrap là giải pháp tốt hơn

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

Đề xuất hiện đại là sử dụng NSURL cho các tệp và thư mục thay vì các đường dẫn dựa trên NSString:

nhập mô tả hình ảnh ở đây

Vì vậy, để có được thư mục Tài liệu cho ứng dụng dưới dạng 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
}

Điều này có xử lý lỗi thô sơ, vì loại đó phụ thuộc vào ứng dụng của bạn sẽ làm gì trong những trường hợp như vậy. Nhưng điều này sử dụng URL tệp và api hiện đại hơn để trả về URL cơ sở dữ liệu, sao chép phiên bản ban đầu ra khỏi gói nếu nó chưa tồn tại hoặc không có trong trường hợp có lỗi.


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

Thông thường tôi sử dụng tiện ích mở rộng này:

Swift 3.x và 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]
    }
}

gọi cái này ở đâu
B.Saravana Kumar

Đối với mọi phần của mã bạn cần : let path = FileManager.documentsDir()+("/"+"\(fileName)"), bạn có thể gọi nó mà không có sự khác biệt giữa luồng (chính hoặc nền).
Alessandro Ornano

14

Đối với tất cả những người có ví dụ hoạt động với Swift 2.2, mã Abizern với hiện đại hãy thử xử lý lỗi

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
}

Cập nhật Tôi đã bỏ lỡ rằng swift 2.0 mới có bảo vệ (Ruby trừ khi tương tự), vì vậy với bảo vệ, nó ngắn hơn và dễ đọc hơn nhiều

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

Phương pháp Swift 3 tiện lợi hơn :

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

1
Hoặc thậm chíFileManager().urls(for: .documentDirectory, in: .userDomainMask).first!
Iulian Onofrei

7
Tôi không nghĩ cần phải khởi tạo trình quản lý tệp mới mỗi lần chúng tôi muốn nhận URL cho thư mục Tài liệu.
Andrey Gordeev

1
Tôi nghĩ đó là điều tương tự. Cảm ơn!
Iulian Onofrei

5

Xcode 8b4 Swift 3.0

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

3

Thông thường tôi thích như dưới đây trong swift 3 , bởi vì tôi có thể thêm tên tệp và tạo tệp dễ dàng

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
absoluteStringsai. để có được những đường dẫn tập tin fro một fileURL bạn cần phải nhận được nó .pathbất động sản
Leo Dabus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.