如何在Swift中找到NSDocumentDirectory?


162

我正在尝试使用代码获取Documents文件夹的路径:

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

但是Xcode给出了错误: Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'

我试图了解代码中的错误。


1
这个问题有几处修改,增加了可能的解决方案。整个事情一团糟。为了清楚起见,我已回滚到第一个版本。答案不属于问题,应将其发布为答案。我可以讨论是否有人认为我的回滚过于激进。谢谢。
埃里克·艾雅

Answers:


254

显然,编译器认为NSSearchPathDirectory:0是一个数组,并且当然希望使用该类型NSSearchPathDirectory。当然不是有用的错误消息。

但至于原因:

首先,您混淆了参数名称和类型。看一下函数定义:

func NSSearchPathForDirectoriesInDomains(
    directory: NSSearchPathDirectory,
    domainMask: NSSearchPathDomainMask,
    expandTilde: Bool) -> AnyObject[]!
  • directory并且domainMask是名称,您正在使用类型,但是无论如何您都应该将它们留给函数使用。它们主要用于方法中。
  • 另外,Swift是强类型的,因此您不应仅使用0。而应使用枚举的值。
  • 最后,它返回一个数组,而不仅仅是一个路径。

因此,我们有了(针对Swift 2.0更新):

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

对于Swift 3:

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

此答案在Xcode 6.0中失败。演员必须是NSString而不是String
Daniel T.

1
@DanielT。只需再次尝试即可String在Xcode 6.0和6.1中使用。一般情况下,StringNSString在雨燕自动桥接。也许您不得不NSString出于另一个原因而选择使用。
nschum 2014年

您是在操场上还是在实际应用中尝试过?结果是不同的。该代码强制转换为String操场上的,而不是应用程序中的。检查出的问题(stackoverflow.com/questions/26450003/...
丹尼尔T.

两者都是。我想这可能是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的路径:

在此处输入图片说明

因此,以NSURL的形式获取应用程序的Document目录:

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,如果捆绑包中不存在初始版本,则从捆绑包中复制初始版本;如果出现错误,则将其复制为nil。


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]
    }
}

在哪里叫这个?
B.Saravana Kumar,

对于您的代码的每个部分,您都需要: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 
}


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 Dabus '17
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.