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

回答:


254

どうやら、コンパイラNSSearchPathDirectory:0は配列であると考え、当然のことながら型NSSearchPathDirectoryを期待しています。確かに役に立たないエラーメッセージです。

しかし、理由については:

最初に、引数の名前と型を混同しています。関数の定義を見てみましょう:

func NSSearchPathForDirectoriesInDomains(
    directory: NSSearchPathDirectory,
    domainMask: NSSearchPathDomainMask,
    expandTilde: Bool) -> AnyObject[]!
  • directorydomainMask名前であり、タイプを使用していますが、とにかく関数の場合は省略してください。それらは主にメソッドで使用されます。
  • また、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なくto でなければなりませんString
Daniel T.

1
@DanielT。もう一度試してみてString、Xcode 6.0および6.1で動作します。一般的に、StringそしてNSStringスウィフトに自動的にブリッジされます。多分あなたはNSString別の理由でキャストしなければなりませんでした。
nschum 2014年

遊び場や実際のアプリで試しましたか?結果は異なります。コードStringはプレイグラウンドでにキャストしますが、アプリではキャストしません。質問(チェックアウトstackoverflow.com/questions/26450003/...
ダニエルT.

両方です。Swiftの微妙なバグである可能性があります。おそらく安全のために回答を編集します。:)ありがとう
nschum 2014年

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

最近の推奨事項は、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•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)")、あなたはスレッド(メインまたはバックグラウンド)間の違いなしでそれを呼び出すことができます。
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にガード(Ruby以外はR​​uby)があることをお見逃しました。

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 2016年

7
DocumentsディレクトリのURLを取得するたびに、新しいファイルマネージャーをインスタンス化する必要はないと思います。
Andrey Gordeev 2016年

1
同じことだと思いました。ありがとう!
Iulian Onofrei 2016年

5

Xcode 8b4 Swift 3.0

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

3

ファイル名を追加して簡単にファイルを作成できるので、通常、私は以下のように迅速に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間違っている。.pathプロパティを取得するために必要なfileURLからファイルパスを取得するには
Leo Dabus
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.