📝

Swift FileManager入門

2024/11/21に公開

概要

Swiftでのファイル操作について学習したので、まとめてみました。 
まず、パスの取得や結合の方法を確認し、その後ファイルやディレクトリの操作を
みていきます。

ドキュメントディレクトリ

初めにパスの取得や生成の基本となるドキュメントディレクトリについて確認していきたいと思います。
ドキュメントディレクトリとは、各アプリケーションがユーザーのデータを保存するために使用する特定のフォルダのことです。(他にも種類があるので、気になる方は調べてみてください)
ファイル操作の際は、このドキュメントディレクトリのパスにパスの一部を追加してフルパスを作成します。
パスの最後がアプリケーションのid/Documentsになっています。
またSwiftでは、パスはURL型として扱います。

パスの取得と結合

ドキュメントディレクトリパスの取得

let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first // URL?
// file:///Users/xxx/Library/Developer/CoreSimulator/Devices/C4D60D65-E331-43E9-9F9D-43F78A5F2CBF/data/Containers/Data/Application/4CD67F84-BDD0-4790-9ABA-3959A2CDCAC2/Documents/

let path = documentDirectroy.path()
/var/mobile/Containers/Data/Application/580FEE2F-F248-45BA-88FB-5E8DE83EA1B3/Documents/

FileManagerのurls(for:in:)でドキュメントディレクトリのパスが取得できます。
返り値はURL?なのでアンラップする必要があります。
また、pathメソッドでURL型からStringのパスへ変換できます。

パスの結合

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

        let folderName = "MyFolder"
        let fileName = "example.txt"

        // ディレクトリのURL
        let folderURL = documentDirectory.appending(path: folderName, directoryHint: .isDirectory)
file:///var/mobile/Containers/Data/Application/21B11FA6-F214-4651-B899-C24617D55ADB/Documents/MyFolder/

        // ファイルのURL
        let fileURL = documentDirectory.appending(path: fileName, directoryHint: .notDirectory)
file:///var/mobile/Containers/Data/Application/21B11FA6-F214-4651-B899-C24617D55ADB/Documents/example.txt

appending(path:directoryHint:)メソッドでURL型のパスに対して、pathを結合することができます。directoryHintには、ディレクトリかどうかを指定します。

deprecated

下記のメソッドは、将来的にdeprecatedになります。

let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let path = appDirectory.appendingPathComponent("images/image001.jpg")

// file:///Users/xxx/Library/Developer/CoreSimulator/Devices/C4D60D65-E331-43E9-9F9D-43F78A5F2CBF/data/Containers/Data/Application/438E0629-8E75-4838-87A7-535514DA6734/Documents/images/image001.jpg

appendingPathComponentはURL型のメソッドで、パスの一部を引数として受け取って
合成したURL型を返します。
pathComponentがDirectoryだと、事前にわかる場合は、appendingPathComponent(_:isDirectory:)を利用しましょう
なお、appendingPathComponent(_ pathComponent:)はpathComponentがDirectory
の場合、pathの最後に/を入れてくれます。

ファイル操作

以降はドキュメントディレクトリの取得は下記の関数を使用します。
サンプルのDataはStringになっていますが、Data型も同様に書き込み処理等ができます。

func getDocumentDirectory() -> URL {
        return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
}

ファイルの書き込み

func writeFile() {

// ファイルパスの生成
    let fileURL = getDocumentDirectory().appending(path: "example.txt", directoryHint: .notDirectory)
    let content = "Hello, World!"
    
    do {
// ファイルへ書き込み
        try content.write(to: fileURL, atomically: true, encoding: .utf8)
        print("File written successfully to \(fileURL)")
    } catch {
        print("Failed to write to file: \(error)")
    }
}

ファイルの読み取り

func readFile() {
// ファイルパスの生成
    let fileURL = getDocumentDirectory().appending(path: "example.txt", directoryHint: .notDirectory)

// ファイルの存在確認
    if !FileManager.default.fileExists(atPath: fileURL.path) {
        print("File does not exist at \(fileURL).")
        return // ここで処理を終了
    }
    
    do {
// ファイルの中身の読み取り
        let content = try String(contentsOf: fileURL, encoding: .utf8)
        print("File content: \(content)")
    } catch {
        print("Failed to read file: \(error)")
    }
}

ファイルの削除

func deleteFile() {
        let fileManager = FileManager.default
        // ファイルパスの生成
        let fileURL = getDocumentDirectory().appending(path: "example.txt", directoryHint: .notDirectory)

        // ファイルの存在確認
        if !fileManager.fileExists(atPath: fileURL.path) {
            print("File does not exist at \(fileURL).")
            return // ここで処理を終了
        }

        do {
            // ファイルの削除
            try fileManager.removeItem(at: fileURL)
            print("File deleted successfully.")
        } catch {
            print("Failed to delete file: \(error)")
        }
    }

ファイルのコピー

func copyFile() {
    let fileManager = FileManager.default
    let sourceURL = getDocumentDirectory().appending(path: "example.txt", directoryHint: .notDirectory)
    let destinationURL = getDocumentDirectory().appending(path: "example_copy.txt", directoryHint: .notDirectory)
    
    do {
        // コピー先に同名のファイルが存在する場合、削除する
        if fileManager.fileExists(atPath: destinationURL.path) {
            try fileManager.removeItem(at: destinationURL)
        }
        
        // ファイルをコピー
        try fileManager.copyItem(at: sourceURL, to: destinationURL)
        print("File copied successfully to \(destinationURL)")
    } catch {
        print("Failed to copy file: \(error)")
    }
}

ファイル名の変更

func renameFile() {
    let fileManager = FileManager.default
    let originalFileURL = getDocumentDirectory().appending(path: "example.txt", directoryHint: .notDirectory)
    let newFileURL = getDocumentDirectory().appending(path: "renamed_example.txt", directoryHint: .notDirectory)
    
    do {
        // ファイル名を変更する(moveItemを使用)
        if fileManager.fileExists(atPath: originalFileURL.path) {
            try fileManager.moveItem(at: originalFileURL, to: newFileURL)
            print("File renamed successfully to \(newFileURL.lastPathComponent)")
        } else {
            print("Original file does not exist.")
        }
    } catch {
        print("Failed to rename file: \(error)")
    }
}

ディレクトリ操作

ディレクトリの作成

func createDirectory(named directoryName: String) {
    let directoryURL = getDocumentDirectory().appending(path: directoryName, directoryHint: .isDirectory)
    
    do {
        try FileManager.default.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
        print("Directory created successfully at \(directoryURL)")
    } catch {
        print("Failed to create directory: \(error)")
    }
}

ディレクトリの読み取り

func listContents(of directoryName: String) {
    let directoryURL = getDocumentDirectory().appending(path: directoryName, directoryHint: .isDirectory)
    
    do {
        let contents = try FileManager.default.contentsOfDirectory(at: directoryURL, includingPropertiesForKeys: nil)
        print("Contents of directory \(directoryName):")
        for url in contents {
            print(url.lastPathComponent)
        }
    } catch {
        print("Failed to list directory contents: \(error)")
    }
}

ディレクトリの削除

func deleteDirectory(named directoryName: String) {
    let directoryURL = getDocumentDirectory().appending(path: directoryName, directoryHint: .isDirectory)
    
    do {
        try FileManager.default.removeItem(at: directoryURL)
        print("Directory deleted successfully.")
    } catch {
        print("Failed to delete directory: \(error)")
    }
}

ディレクトリ名の変更

func renameDirectory(from oldDirectoryName: String, to newDirectoryName: String) {
    let fileManager = FileManager.default
    let oldDirectoryURL = getDocumentDirectory().appending(path: oldDirectoryName, directoryHint: .isDirectory)
    let newDirectoryURL = getDocumentDirectory().appending(path: newDirectoryName, directoryHint: .isDirectory)
    
    do {
        // 移動(名前変更)
        try fileManager.moveItem(at: oldDirectoryURL, to: newDirectoryURL)
        print("Directory renamed successfully from \(oldDirectoryName) to \(newDirectoryName)")
    } catch {
        print("Failed to rename directory: \(error)")
    }
}

ディレクトリの移動

func moveDirectory(from oldDirectoryName: String, to newDirectoryName: String) {
    let fileManager = FileManager.default
    let oldDirectoryURL = getDocumentDirectory().appending(path: oldDirectoryName, directoryHint: .isDirectory)
    let newDirectoryURL = getDocumentDirectory().appending(path: newDirectoryName, directoryHint: .isDirectory)
    
    // 存在チェック
    if !fileManager.fileExists(atPath: oldDirectoryURL.path) {
        print("Old directory does not exist.")
        return
    }
    
    do {
        // 移動
        try fileManager.moveItem(at: oldDirectoryURL, to: newDirectoryURL)
        print("Directory moved successfully from \(oldDirectoryName) to \(newDirectoryName)")
    } catch {
        print("Failed to move directory: \(error)")
    }
}

以上、ファイル操作の基本をまとめてみました。
実際にファイルが作成されたか、作成されたファイルの中身を確認したい方は
こちらの記事もあわせてご確認ください。

https://zenn.dev/tomo0015/articles/ce8e6d470449d0

最後に

最後までお読みいただきありがとうございます。
間違いなどありましたら、ご指摘いただければ幸いです。

参考記事

https://appdev-room.com/swift-file-manager

Discussion