Swift에서 NSDocumentDirectory를 찾는 방법은 무엇입니까?


162

코드가있는 Documents 폴더의 경로를 얻으려고합니다.

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

그러나 Xcode는 오류를 제공합니다. Cannot convert expression's type 'AnyObject[]!' to type 'NSSearchPathDirectory'

코드에서 무엇이 잘못되었는지 이해하려고합니다.


1
가능한 해결책을 추가하는이 질문에 대한 몇 가지 수정 사항이있었습니다. 모든 것이 엉망이었습니다. 명확성을 위해 첫 번째 버전으로 롤백했습니다. 답변은 질문에 속하지 않으며 답변으로 게시해야합니다. 누군가 내 롤백이 너무 급진적이라고 생각하는지 토론 할 수 있습니다. 감사.
Eric Aya

답변:


256

분명히 컴파일러 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]

스위프트 3

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

이 답변은 Xcode 6.0에서 실패했습니다. 캐스트가 NSString아닌이어야 String합니다.
Daniel T.

1
@DanielT. 다시 시도하고 StringXcode 6.0 및 6.1에서 작동합니다. 일반적으로, String그리고 NSString스위프트 자동으로 브리지됩니다. 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

스위프트 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

현대적인 권장 사항은 NSString 기반 경로 대신 파일 및 디렉토리에 NSURL을 사용하는 것입니다.

여기에 이미지 설명을 입력하십시오

따라서 앱의 문서 디렉토리를 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을 반환하고 번들이없는 경우 번들에서 초기 버전을 복사하거나 오류가 발생하면 nil을 복사합니다.


24

Xcode 8.2.1 • 스위프트 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

일반적으로이 확장을 사용하는 것이 좋습니다.

스위프트 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)"), 스레드 (메인 또는 백그라운드)의 차이없이 호출 할 수 있습니다.
Alessandro Ornano

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에는 가드 (아날로그가 아닌 경우 루비)가 있으므로 가드를 사용하면 훨씬 짧고 읽기 쉽습니다.

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
Documents 디렉토리의 URL을 얻을 때마다 새 파일 관리자를 인스턴스화 할 필요가 없다고 생각합니다.
Andrey Gordeev

1
나는 그것이 같은 것이라고 생각했다. 감사!
Iulian Onofrei

5

Xcode 8b4 스위프트 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속성을
가져와야
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.