จะค้นหา 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
Daniel T.

1
@DanielT เพียงแค่พยายามอีกครั้งและStringทำงานใน Xcode 6.0 และ 6.1 โดยทั่วไปStringและNSStringมีการ bridged โดยอัตโนมัติในสวิฟท์ บางทีคุณต้องโยน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
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

ข้อเสนอแนะที่ทันสมัยคือการใช้ NSURLs สำหรับไฟล์และไดเรกทอรีแทน NSString เส้นทางตาม:

ป้อนคำอธิบายรูปภาพที่นี่

ดังนั้นเพื่อให้ได้เอกสารไดเรกทอรีสำหรับ app เป็น 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

ฉันมักจะ prefeer ที่จะใช้ส่วนขยายนี้:

สวิฟท์ 3.x และสวิฟท์ 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]
    }
}

จะโทรหาที่ไหน
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
}

อัปเดต ฉันพลาดที่รวดเร็วใหม่ 2.0 มียาม (ทับทิมเว้นแต่อนาล็อก) ดังนั้นกับยามมันเป็นมากสั้นและอ่านได้มากขึ้น

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!
Iulian Onofrei

7
ฉันไม่คิดว่าจำเป็นต้องสร้างอินสแตนซ์ของตัวจัดการไฟล์ใหม่ทุกครั้งที่เราต้องการรับ URL สำหรับไดเรกทอรีเอกสาร
Andrey Gordeev

1
ฉันคิดว่ามันเหมือนกัน ขอบคุณ!
Iulian Onofrei

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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.