FileManagerを使ってファイルやフォルダを保存する

## この記事の中で書くこと

FileManagerを使うと、画像や音声、動画ファイルを端末に保存することができます。ここではAppDataのDocumentsやそこに作成されたフォルダに任意のファイルやフォルダを作成する方法について共有できればと思います。

![スクリーンショット 2021-01-03 23.30.27.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549093/48d15ff0-bb49-7263-61b5-d5bf315ad69f.jpeg)

フォルダの構成やそれぞれの役割などについては以下のサイトが参考になります。

[iOSアプリのファイル保存について](https://qiita.com/nnsnodnb/items/13642c4a8d55641f893e)



## Documentsの中にテキストファイルを保存する

### 目標

```
iOS App
    ├── Documents --- test.txt //これを追加する
    ├── Library
    │   ├── Caches
    │   └── Preferences
    └── tmp
```

### ソースコード

```swift
func addfile() {
        var documentDirectoryFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let filename = "test.txt"
        let path = documentDirectoryFileURL.appendingPathComponent(filename)
        documentDirectoryFileURL = path
        let testText = "This is a pen"
        let data: Data? = testText.data(using: .utf8)
        guard let textFile = data else { return }
        do {
            try textFile.write(to: documentDirectoryFileURL)
            print("成功した")
        } catch {
            print("失敗した")
        }
    }
```

### 流れ

2行目の`FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]`の内容を出力すると

**file:///var/mobile/Containers/Data/Application/DA078380-F632-4FF0-A96F-F68E036E1B39/Documents/**

と出ます。要はDocumentsフォルダまで辿るためのURL型のコードを用意しています。

その後、今回用意するテキストファイルを`test.txt`として用意します。

そして`appendingPathComponent`によって、Documentsフォルダからさらに次のファイルまで辿れるようにURLを変更します。

6行目ではテキストに保存する内容`testText`を用意しています。大抵の場合、保存する内容はフォルダに保存する関数の中で用意されないと思うので、変更を加える部分はここになるでしょう。

その後、その`testText`をデータ型に変換したのち、前半部分で用意したフォルダに`write`します。



## Documentsの中にフォルダを保存する

Documentsフォルダにさらにフォルダを追加します。

### 目標

```
iOS App
    ├── Documents --- TestDirectory //これを追加する
    ├── Library
    │   ├── Caches
    │   └── Preferences
    └── tmp
```

### ソースコード

```swift
func addFolder() {
        let fileManager = FileManager.default
        let documentDirectoryFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let directory = documentDirectoryFileURL.appendingPathComponent("TestDirectory", isDirectory: true)
        do {
            try fileManager.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
        } catch {
            print("失敗した")
        }
    }
```

### 流れ

3行目まではここまで説明した内容と重複しているので、説明は省略します。

4行目は`appendingPathComponent`でDocumentsにパスを追加しています。今回は追加しているのがファイルではなくフォルダなので、`isDirectory`パラメータを追加し、`true`にしています。

その後`fileManager.createDirectory`を行い、フォルダを追加します。





## Documentsに追加したフォルダの中にファイルを追加する

```
iOS App
    ├── Documents--- TestDirectory --- test.txt  //これを追加する
    ├── Library
    │   ├── Caches
    │   └── Preferences
    └── tmp
```

ここまでの知識を総動員していきます。

### ソースコード

Documentにフォルダを追加するための関数`addFolder`と、その関数を用いて追加されたフォルダの先にファイルを追加する関数``を作成しました。

```swift
func addFolder(_ folderName: String) {
        let fileManager = FileManager.default
        let documentDirectoryFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let directory = documentDirectoryFileURL.appendingPathComponent(folderName, isDirectory: true)
        do {
            try fileManager.createDirectory(at: directory, withIntermediateDirectories: true, attributes: nil)
        } catch {
            print("失敗した")
        }
}

func addfileToFolder(_ folderName: String, _ fileName: String) {
        var pathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        //フォルダを追加する
        addFolder(folderName)
        //パスを用意する
        pathString = "file://" + pathString + "/" + "\(folderName)/"
        guard let path = URL(string: pathString) else { return }
        //保存するファイルを用意する
        let fileName = "\(fileName).txt"
        let textPath = path.appendingPathComponent(fileName)
    
        //内容を用意する
        let testText = "This is a pen"
        let data: Data? = testText.data(using: .utf8)
        guard let textFile = data else { return }
        //パスに内容を書き込む
        do {
            try textFile.write(to: textPath)
            print("書き込みに成功した")
        } catch let error {
            print("書き込みに失敗した\(error)")
        }
}
```

### 流れ

#### addFolder

これは上述したDocumentsの先にフォルダを追加する`addFolder`にString型の引数を持たせることで一般性を高めたものになります。もしDocumentsの中に指定した名前のフォルダが既に存在していたとしても、新たに追加されることはありませんでした。(確認済)

#### addfileToFolder

Documentsの先に作成する、またはそこまでパスを辿るフォルダの名前と、そのフォルダの先に追加するファイルの名前を引数に持っています。

17行目の`pathString`はDocumentsまでのパスをURL型ではなくStirng型で用意しています。

保存するフォルダとそこまでのパスが用意できた後はこれまでの内容と同じことを行なっています。