【Swift】FileManagerでファイルを保存!操作方法や格納場所

この記事からわかること

  • SwiftFileManagerとは?
  • 使い方設定方法メリット
  • iOSファイルシステム:サンドボックス構造とは?
  • 格納できるディレクトリ種類

index

[open]

\ アプリをリリースしました /

ふるログ

ふるさと納税管理アプリ-ふるログ-

無料posted withアプリーチ

Swiftでファイルをディレクトリ内に保存する方法をまとめていきたいと思います。

Swiftでファイルを保存する際のポイント

Swiftでファイルを保存、読み込み、書き込みなどを行う際のポイントをまずは確認してみます。

ポイント

アプリ内で生成したファイルなどを保存するにはiOSのファイルシステム構造やディレクトリの役割、FileManagerクラスの使い方が重要になってきます。

iOSのファイルシステム:サンドボックス構造

iOSのファイルシステムはサンドボックス構造が採用されています。サンドボックス構造とはサンドボックス(砂場)と呼ばれる外部とは隔離された仮想領域を用意し、その中でのみ動作や操作を許容する構造です。これにより外部の重要なプログラムなどへ悪意あるアクセスや動作が及ばないようにすることができます。

インストールしたiOSアプリはサンドボックス内に格納され動作しています。保存されたデータなどもサンドボックス内に格納され、アンインストール時に保存されたデータなども一緒に削除されます。

iOSアプリでファイルを保存する場合もサンドボックス内の決められたディレクトリ内に保存していきます。

おすすめ記事:【Swift】ファイルアプリからDocumentsフォルダへアクセス許可する方法!

参照できるディレクトリ

ファイルの保存先として使用できるのは下記のような構造になっているディレクトリ群のようです。それぞれに役割が決まっており、適切な場所へのファイルの格納が大事になってきます。Appleが定めているガイドラインに反したファイル管理をしているとアプリの審査がリジェクト(却下)されてしまうこともあるようです。

参考文献:FileSystem Programming Guide

├── AppData
│ ├── Documents
│ ├── Library
│        ├── Application Support
│        ├── Caches
│        ├── Preferences
│        ├── Saved Application State
│        └── SplashBoard
│ ├── SystemData
│ └── tmp

Documents

ユーザーが生成したファイルや画像や動画などのデータなど、ユーザーが閲覧するファイルを保存します。このディレクトリ内はiTunes/iCloudにバックアップされます。

Library

Documentフォルダに格納するデータ以外を保存するためのディレクトリです。ディレクトリの内容はLibrary/Cachesサブディレクトリ以外は、iTunesとiCloudによってバックアップされます。

tmp

テンポラリファイル(temporary files:一時的に作成されるファイル)が保存されるディレクトリです。tmp内のファイルは不要になったら削除することが推奨されています。またアプリが起動していないタイミングで自動で削除される可能性もあります。このフォルダはiTunes/iCloudにバックアップされません。

AppDataディレクトリを確認する方法

AppDataディレクトリを確認する方法

実際にAppDataディレクトリを確認するには少し手順があります。下記の記事を参考にしてください。

おすすめ記事:AppDataディレクトリを確認する方法

FileManagerの使い方

公式リファレンス:FileManager

FileManagerとはファイルやディレクトリに関する操作(パスの取得や作成、削除、コピーなど)を行うことのできるクラスです。このクラスを使用するとDocumentsディレクトリのパスを簡単に取得することも可能になります。

各ディレクトリのパスを取得するにはurls()を使用します。引数for:取得したいパスの値を引数in:には対象のアプリフォルダドメインを指し示す.userDomainMaskを指定します。返り値はオプショナル型の配列なのでfirstで最初の要素を取得し、!でアンラップするとURL型のパスが取得できます。

Documentsディレクトリのパス

FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!

Libraryディレクトリのパス

FileManager.default.urls(for: .libraryDirectory, in: .userDomainMask).first!

Library/cacheディレクトリのパス

FileManager.default.urls(for: .cacheDirectory, in: .userDomainMask).first!

tmpディレクトリのパス

NSTemporaryDirectory()

Homeディレクトリのパスと応用

NSHomeDirectory()  // Homeディレクトリのパス
NSHomeDirectory() + "/Documents" // Documentsディレクトリのパス
NSHomeDirectory() + "/Library"   // Libraryディレクトリのパス

NSHomeDirectory()を使って構築したディレクトリへのパスはString型ですが、FileManagerを使って取得したパスはURL型になります。

let str =  NSHomeDirectory() + "/Documents"
let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
dump(type(of: str)) // String.Type
dump(type(of: url)) // Foundation.URL.Type

URL型とは?

SwiftではInt型やString型以外にURL型というデータ型があります。FileManagerを使わないで変数にURL型として格納する際は型の宣言だけでなくURL()で囲うことで内部のイニシャライザーが動作し、String型をURL型に変換します。

let url : URL = URL(string:"https://www.amefure.com")!

URL()で囲うとオプショナルバリューになるので!を使って強制的にアンラップしておきます。

ファイルの書き込み処理

引数に受け取ったテキストをDocumentsディレクトリ内の「sample.txt」に書き込み処理を行うwritingFile関数を定義してみます。

func writingFile(_ text:String) {
    // Documentsディレクトリまでのパスを生成
    guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else{
        fatalError("URL取得失敗")
    }
    // ファイル名を含めたフルパスを生成
    let fullURL = docURL.appendingPathComponent("sample.txt")

    do {
        // 書き込み処理
        try text.write(to: fullURL,atomically: true,encoding: .utf8)
    } catch{
        print("書き込み失敗")
    }         
}

FileManagerはまずdefaultでFileManagerオブジェクトを生成できます。さらにurlsで引数for:に指定したパスを取得することができます。以下のようにコードを分割しても同意です。urlsで帰ってくるのはオプショナルバリューとなったURL型なのでアンラップしないといけない点に注意してください。

let fileManager = FileManager.default
let docURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
// Optional(file:///〜

おすすめ記事:Optional(オプショナル)型とnilとは?

appendingPathComponentはURLに追記処理ができます。これでDocuments/sample.txtまでのフルパスが完成します。writeで引数toに指定したパスに書き込み処理を行えます。該当ファイルが存在しない場合は自動で生成して書き込み処理を行います。atomicallyは書き込み中にエラーが発生してもファイルが壊れないように一時的なファイルを作成してくれます。

おすすめ記事:【Swift/String】write(to:atomically:encoding:)メソッドでファイルに文字列を書き込む方法!

ファイルの読み込み処理

Documentsディレクトリ内の「sample.txt」を読み込み内容を戻り値として返すreadingFile関数を定義してみます。

func readingFile()->String?{
    guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else{
        fatalError("URL取得失敗")
    }
    let fullURL = docURL.appendingPathComponent("sample.txt")
    do {
        let textData = try String(contentsOf: fullURL, encoding: .utf8)
        return textData
    } catch {
        return nil
    }
}

ファイルの読み込み処理はString型にキャストできるString()で行うことができます。内部のイニシャライザーが引数contentsOf: URLに指定されているURLのデータを取得し文字列として返してくれます。

ファイルの削除処理

Documentsディレクトリ内の「sample.txt」を削除するremoveFile関数を定義してみます。削除処理はfileManager.removeItemで実行できるのでfileManagerにFileManagerオブジェクトを格納して呼び出しやすくしておきます。

func removeFile(){
    let fileManager = FileManager.default
    guard let docURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else{
        fatalError("URL取得失敗")
    }
    let fullURL = docURL.appendingPathComponent("sample.txt")
    do {
        try fileManager.removeItem(at: fullURL)
    } catch {
        print(error.localizedDescription)
    }
}

ファイルのコピー処理

Documentsディレクトリ内の「sample.txt」をコピーするcopyFile関数を定義してみます。コピー処理はfileManager.copyItemで実行できます。引数にコピー元とコピー先のURL型のパスを渡すだけです。

func copyFile(){
    let fileManager = FileManager.default
    guard let atURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else{
        fatalError("URL取得失敗")
    }
    guard let toURL = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else{
        fatalError("URL取得失敗")
    }
    let fullAtURL = atURL.appendingPathComponent("sample.txt")
    let fullToURL = toURL.appendingPathComponent("/test/sample.txt")
    do {
        try fileManager.copyItem(at: fullAtURL, to: fullToURL)
    } catch {
        print(error.localizedDescription)
    }
}

空のファイルの作成処理

指定したパスに空のファイルを作成するならcreateFileメソッドを使用します。引数atPathにはString型でファイルパスを渡しますcontentsにはnilを渡せば空のファイルが、何かしらのデータを渡せば書き込まれた状態のファイルが生成されます。

func createFile() {
    let fileManager = FileManager.default
    // String型のDocPathを作成
    let docPath =  NSHomeDirectory() + "/Documents"
    let filePath = docPath + "/sample.txt"
    
    if !fileManager.fileExists(atPath: filePath) {
        fileManager.createFile(atPath:filePath, contents: nil, attributes: [:])
    }else{
        print("既に存在します。")
    }
}

ファイルが存在するかどうか

指定したファイルパスにファイルがあるかどうかを識別するにはfileExistsメソッドを使用します。引数atPathにはString型でファイルパスを渡します。存在すればtrueを返します。

先ほどの空のファイル作成処理ではファイルが既に存在する場合は作成処理を行わないようにしています。

if !fileManager.fileExists(atPath: filePath) {
    fileManager.createFile(atPath:filePath, contents: nil, attributes: [:])
}else{
    print("既に存在します。")
}

エラーの意味

The file “sample.txt” doesn’t exist.
// ファイル「sample.txt」は存在しません。
The file “sample.txt” couldn’t be opened because there is no such file.
// そのようなファイルがないため、ファイル「sample.txt」を開くことができませんでした

私がSwift UI学習に使用した参考書

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

自作iOSアプリ

\ アプリをリリースしました /

mapping

自分だけの地図を作ろう!-mapping-

無料posted withアプリーチ

割り勘アプリ-bill-

旅行におすすめ!
割り勘アプリ-bill-

無料posted withアプリーチ

Imakoko

現在地を取得するアプリ!Imakoko

無料posted withアプリーチ

ふるログ

ふるさと納税管理アプリ-ふるログ-

無料posted withアプリーチ

Remind-シンプル通知アプリ-

シンプル通知アプリ-Remind-

無料posted withアプリーチ

CLIPURL

好きな記事をクリップしよう!-CLIPURL-

無料posted withアプリーチ

記録カレンダー

続けたを可視化できるアプリ!記録カレンダー

無料posted withアプリーチ

CART-共有できるお買い物リスト-

CART-共有できるお買い物リスト-

無料posted withアプリーチ

Githubにて
iOSアプリのソースコードを公開中!

自作Webアプリ

子育て知識共有サイト-mikata-

子育て知識共有サイト-mikata-

フレームワーク:Laravel/Vue.js

作成の流れQiita「Laravel×Vue.jsを使って初めてWebアプリを自作しました!」

感想:初めて作成したWebアプリです!メールアドレスでの会員登録や質問投稿、回答やコメント、いいねやフォローなどSNSに近い機能を実装してみました。レビューや修正すべきポイントなどを教えていただけると嬉しいです!

New Article

index