LoginSignup
57
59

More than 5 years have passed since last update.

[iOS] ファイル操作が劇的に簡単になる便利ライブラリ!

Last updated at Posted at 2015-01-11

iOSでのファイル操作する際、NSFileManagerを使うのですが、それぞれのファイル操作はかなり癖があるAPIで中々扱いづらいです。特にSwiftから使うと違和感が大きいです
ということで!簡単にファイルを扱えるようなNSFileManagerのラッパーライブラリを作ってみました!

SwiftFilePath

導入

CocoaPodsでインストール

CocoaPodsのverisonが0.36以上の環境でPodfileに以下を記載してpod updateすればOKです!

Podfile
pod 'SwiftFilePath'

( 注:0.36はプレリリースのためgem install cocoapods --preでCocoaPodsをアップデートしておく必要があります )

Carthageでインストール

Cartfileに以下を記載してcarthage updateすればOKです!

Cartfile
github "nori0620/SwiftFilePath"

便利さ

利用するにはファイルの上に以下のimport文を書きます。

各ファイル
import SwiftFilePath

importすると、Pathというクラスが利用できるようになります。
例えば、アプリのDocumentsディレクトリ以下にあるサブディレクトリだけ(=ファイルを除く)の名前一覧を表示したいとします。
SwiftFilePathを使うと以下の1行だけ書けます!(println抜きだと1行)

SwiftFilePathを使った場合
// 便利!!!( SwiftFilePathをつかった場合 )
let dirs = Path.documentsDir.contents!.filter({$0.isDir}).map({$0.basename })
println(dirs)  // 結果:[VideoDir, TextDir, PhotoDir]

ちなみに上と同様の処理をライブラリを使わずにNSFileManagerを使って書くと以下のようになります。

SwiftFilePathを使わない場合
// つらい!!!( 上と同様の処理をNSFileManagerを直接使って作った場合 )
let fileManager = NSFileManager.defaultManager()
let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let contents =   fileManager.contentsOfDirectoryAtPath(documentsPath, error: nil)
let dirs = contents!.filter({ file  in
    let filePath = documentsPath.stringByAppendingPathComponent(file as String)
    var isDirectory: ObjCBool = false
    let isFileExists = NSFileManager.defaultManager().fileExistsAtPath(filePath, isDirectory:&isDirectory)
    return isDirectory ? true : false // ObjcBool -> bool
})
println(dirs)  // 結果:[VideoDir, TextDir, PhotoDir]

というわけでSwiftFilePathを使うとNSFileManagerのレガシー感が隠蔽されるのでツラさが減ります。

具体的な使い方

オブジェクトの作成

Pathクラスに文字列を渡すと、ファイルのパスを表すPathオブジェクトが生成されます。

Pathオブジェクトの作成
let fooDir = Path("/path/to/fooDir")

// toStringを呼ぶと元の文字列が取得できます
fooDir.toString() // "/path/to/fooDir"

もちろん、パスを表現しているクラスなので、存在しないファイルやディレクトリに対しても作成することができます!

そしてアプリ内から、よく利用する各ディクトリに関しては取得するためのショートカットメソッドを用意しています。

アプリから利用できるディレクトリ
Path.homeDir.toString()
// "/var/mobile/Containers/Data/Application/<UUID>"

Path.documentsDir.toString()
// "/var/mobile/Containers/Data/Application/<UUID>/Documents"

Path.cacheDir.toString()
// "var/mobile/Containers/Data/Application/<UUID>/Library/Caches"

Path.temporaryDir.toString()
// "var/mobile/Containers/Data/Application/<UUID>/Library/tmp"

パス操作

[]を使ってディレクトリの中の要素のPathオブジェクトを取得できます。便利!

// ドキュメントディレクトリの中のfoo.txtのPathを表すオブジェクトを取得
let textFilePath = Path.documentsDir["foo.txt"]
textFilePath.toString() //  "~/Documents/foo.txt"

// 階層になっていてもアクセスできる
let jsonFilePath = Path.documentsDir["subdir"]["bar.json"]
jsonFilePath.toString() //  "~/Documents/subdir/bar.json"

preantは親ディレクトリのPathオブジェクトを持っています。

// parentで親ディレクトリのPathオブジェクトを取得
jsonFilePath.parent.toString() // "~/Documents/subdir"
jsonFilePath.parent.parent.toString() // "~/Documents"
jsonFilePath.parent.parent.parent.toString() // "~/"

contentsメソッドで、ディレクトリの中身一覧をPathオブジェクトとして取得できます。

// ディレクトリの中身一覧をPathオブジェクトで取得
let contents = Path.homeDir.contents!
//  Pathオブジェクト化されたhomeディレクトリの中身が配列に入っている
// [
//    Path<~/.com.apple.mobile_container_manager.metadata.plist>, 
//    Path<~/Documents>,
//    Path<~/Library>, 
//    Path<~/tmp>,
// ]

さらに!フォルダを指すPathオブジェクトは、それ自体をfor inIteratorとして扱うこともできます。

for content:Path in Path.homeDir { println(content) }

ファイルの情報の取得

isDirでフォルダかどうかの判定ができます。

Path.homeDir.isDir // true
Path.homeDir["hoge.txt"].isDir //false

existsはファイルが存在しているかどうかの判定を返します

// ファイルが存在しているかどうかの取得
Path.homeDir.exists // true

// homeDirの中にfoo.txtというファイルが存在するか?
Path.homeDir["foo.txt"].exists

// homeの中のmyDirディレクトリにbar.txtというファイルが存在するか?
Path.homedir["myDir"]["bar.txt"].exists

basenameでファイル名だけを取得できます

Path.homedir["myDir"]["bar.txt"].basename // bar.txt

extはファイルの拡張子を取得できます。

// Documentsディレクトリ内のjsonファイルのみの配列を取得
let allFiles  = Path.documentsDir.contents!
let jsonFiles = allFiles.filter({$0.ext == "json" })

attributesでファイルの属性情報を取得することができます。attributesからアクセスできる全ての要素はこちらの公式ドキュメントを御覧ください。

let jsonFile = Path.documentsDir["foo.json"]
jsonFile.attributes!.fileCreationDate()! // 2015-01-11 11:30:11 +0000
jsonFile.attributes!.fileModificationDate()! // 2015-01-11 11:30:11 +0000
jsonFile.attributes!.fileSize() // 2341

ファイル操作

mkdirでディレクトリ作成、touchで空ファイル作成、removeでファイル削除ができます。

// Documentディレクトリにfooディレクトリを作成
let fooDir = Path.documentsDir["foo"]
fooDir.mkdir()

// fooディレクトリの中に空のhoge.txtを作成
let hogeFile = fooDir["hoge.txt"]
hogeFile.touch()

// ディレクトリごと削除
fooDir.remove()

copyTo/moveToはその名の通り、コピーと移動を行います。

// Documentsのfoo.txtをtmpディレクトリにコピー
let fooFile = Path.documentsDir["foo.txt"]
let destination = Path.tmpDir["foo.txt"]
fooFile.copyTo( destination )

readString/writeStringでファイルのデータを文字列で読み書きできます。

// ファイルへ文字列書き込み
let textFile = Path.documentsDir["hello.txt"]
textFile.writeString("HelloSwift")

// ファイルから文字列読み込み
let text = textFile.readString()! // HelloSwift

readData/writeDataでファイルへデータを書き込めます

//  NSDataの書き込み
let binFile = Path.documentsDir["foo.bin"]
binFile.writeData( NSData()  )

// NSDataの読み込み
let data = binFile.readData()!

エラーハンドリングについて

上では触れていませんでしたがファイルシステムについて、副作用を持つメソッドtouch/remove/copyTo/writeToなどは返り値としてResultオブジェクトが返ってきます。

Resultオブジェクトは操作が成功した場合はvalueに結果の値を持ちます。失敗した場合はerrorにエラーメッセージが入っています。
またisSuccess/isFailureで失敗成功を取得することができます。

例えば以下のようにエラーハンドリングができます。

let result = Path.documentsDir["subdir"].mkdir()
if( result.isSuccess ){ // 成功時
    println( result.value! ) // valueには作成できたフォルダのPathが入っている
}
if( result.isFailure ){ // 失敗時
    println( result.error! ) // errorにはエラーメッセージが文字列で入っている
}

ただ、上の方法はOptional Value!でアンラップしているのが若干気持ちわるいです。そこで以下のような書き方も用意しています。(結果は上の処理と全く同じです )

Path.documentsDir["subdir"].mkdir()
    .onSuccess({ (value:Path) in // 成功時
        println( value )
    })
    .onFailure({ (error:String) in // 失敗時
        println( error )
    })

こちらの書き方だとOptionalValueを強制的にアンラップせずにエラーハンドリングが書けます。

まとめ

以上、SwiftFilePathの紹介でした! NSFileManagerをそのまま使うのに比べ、わかりやすいAPIで様々なことができると思います。 Swiftでファイル操作していてNSFileManagerつらい!となったときは、是非試してみてください!

57
59
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
59