【Swift】CSVファイルを使ってデータの入出力を行う。(Swift 2.1、XCode 7.2)
CSVファイルとは
本記事ではCSVファイルを用いてデータを入出力する方法について説明する。
CSVファイルとは、Comma Separated Valuesの略で、データをカンマ区切りで並べたファイルのことをいう。アプリで利用する可変データをCSVファイルで用意し、アプリ起動時に読み込んだり、次回起動時のために出力したりして利用する。
データ永続化の観点ではCore Dataでも同じことができるが、CSVファイルのほうが手軽に編集できる。また、他のアプリにデータを渡すときもCSVファイル1つを渡すだけでいいので取り扱いが楽だ。
田中,30,野球部
近藤,33,サッカー部
以下のコードのように、アプリで利用するデータをソースコードに直接定義した場合、データ変更時にソースコードを編集する必要があるのと、アプリ使用中にデータを変更して保存することができない。
かといって、データベースを利用するほどのデータでもない。そんなときに利用できるのがCSVファイルである。
1 2 3 4 5 |
//部活一覧 let studentList = [[1, "3A", "野球部"], [2, "2B", "サッカー部"], [3, "6C", "テニス部"]] |
CSVファイルを読み込む
実際にCSVファイルを読み込んで使ってみよう。
以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる人はご利用下さい。
⇒「テスト用プロジェクト」
ソースコードに記述された部活データをテーブルビューに表示するものを実装しておいた。この部活データをCSVファイルから読み込むように変更する。
以下のコードは変更前のViewController.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// // ViewController.swlft // import UIKit class ViewController: UIViewController,UITableViewDataSource { @IBOutlet weak var testTableView: UITableView! //表示データ let dataList = [[1, "3A", "野球部"], [2, "2B", "サッカー部"], [3, "6C", "テニス部"]] //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() } //データを返すメソッド func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell { //セルを取得する。 let cell = tableView.dequeueReusableCellWithIdentifier("TestCell", forIndexPath:indexPath) as UITableViewCell //セルのラベルに部名、部室を設定する。 cell.textLabel?.text = dataList[indexPath.row][2] as? String cell.detailTextLabel?.text = "部室:" + String(dataList[indexPath.row][1]) return cell } //データの個数を返すメソッド func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int { return dataList.count } } |
メニューから「File」⇒「New」⇒「File…」を選択する。
テンプレートを選択する画面が表示されるので、「iOS」⇒「Other」⇒「Empty」を選択し、「Next」ボタンを押す。
ファイル名を入力する画面が表示されるので、Save Asに「sample.csv」、Whereにプロジェクトフォルダを設定して「Create」ボタンを押す。
追加されたCSVファイル(下図赤枠)を選択し、黄緑枠の編集欄にCSVのデータを入力する。
以下は入力するデータ
2,2B,サッカー部
3,6C,テニス部
ViewController.swiftを以下のコードに変更する。ViewDidLoadメソッドでCSVファイルのデータを読み込み、改行区切りで部活配列に格納している。
デリゲートメソッドの「データを返すメソッド」が呼び出されたら、部活配列から抽出したデータをカンマ区切りで分割し、ラベルに設定している。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
// // ViewController.swlft // import UIKit class ViewController: UIViewController,UITableViewDataSource { //部活配列 var dataList:[String] = [] //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() do { //CSVファイルのパスを取得する。 let csvPath = NSBundle.mainBundle().pathForResource("sample", ofType: "csv") //CSVファイルのデータを取得する。 let csvData = try String(contentsOfFile:csvPath!, encoding:NSUTF8StringEncoding) //改行区切りでデータを分割して配列に格納する。 dataList = csvData.componentsSeparatedByString("\n") } catch { print(error) } } //データを返すメソッド func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell { //セルを取得する。 let cell = tableView.dequeueReusableCellWithIdentifier("TestCell", forIndexPath:indexPath) as UITableViewCell //カンマでデータを分割して配列に格納する。 let dataDetail = dataList[indexPath.row].componentsSeparatedByString(",") //セルのラベルに部名、部室を設定する。 cell.textLabel?.text = dataDetail[2] cell.detailTextLabel?.text = "部室:" + String(dataDetail[1]) return cell } //データの個数を返すメソッド func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int { return dataList.count } } |
以下は実際のプレイ動画。ソースコードに直接書いていたデータをCSVファイルから読み込むようにしただけなので、外見上の動きは変更前と変わらない。
CSVファイルを出力する
次回起動時の読み込みデータを変更するために、アプリ内からCSVファイルを更新したいことがある。
テーブルビューに表示されている部活を削除したときに、CSVデータが更新されるものを実装してみよう。
ユーザーデータを保存するDocumentsフォルダにCSVファイルを保存する。アプリ起動時にDocumentsフォルダにsample.csvが存在するかを確認し、存在する場合はそのデータを読み込み、存在しない場合はプロジェクトに最初から格納されているsample.csvを読み込むようにする。
ViewController.swiftを以下のコードに変更する。
テーブルビューからデータを削除すると同時にDocumentsフォルダにsample.csvを出力している。部活をすべて削除した場合はDocumentsフォルダのsample.csvを削除する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
// // ViewController.swlft // import UIKit class ViewController: UIViewController,UITableViewDataSource { @IBOutlet weak var testTableView: UITableView! //部活配列 var dataList:[String] = [] //CSVファイルの保存先 var userPath:String! let fileManager = NSFileManager() //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() do { //ユーザーが保存したCSVファイルのパス userPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] + "/sample.csv" var path = userPath if(fileManager.fileExistsAtPath(path) == false){ //ユーザーが保存したCSVファイルが無い場合は、初期CSVファイルから読み込む。 path = NSBundle.mainBundle().pathForResource("sample", ofType: "csv")! } //CSVファイルのデータを取得する。 let csvData = try String(contentsOfFile:path, encoding:NSUTF8StringEncoding) //改行区切りでデータを分割して配列に格納する。 dataList = csvData.componentsSeparatedByString("\n") //テーブルビューを編集モードにする。 testTableView.editing = true //CSVファイルの出力先を確認する。 print(userPath) } catch { print(error) } } //データを返すメソッド func tableView(tableView:UITableView, cellForRowAtIndexPath indexPath:NSIndexPath) -> UITableViewCell { //セルを取得する。 let cell = tableView.dequeueReusableCellWithIdentifier("TestCell", forIndexPath:indexPath) as UITableViewCell //カンマでデータを分割して配列に格納する。 let dataDetail = dataList[indexPath.row].componentsSeparatedByString(",") //セルのラベルに部名、部室を設定する。 cell.textLabel?.text = dataDetail[2] cell.detailTextLabel?.text = "部室:" + String(dataDetail[1]) return cell } //データの個数を返すメソッド func tableView(tableView:UITableView, numberOfRowsInSection section:Int) -> Int { return dataList.count } //テーブルビュー編集時に呼ばれるメソッド func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { //削除の場合、配列からデータを削除する。 if( editingStyle == UITableViewCellEditingStyle.Delete) { dataList.removeAtIndex(indexPath.row) } //テーブルの再読み込み tableView.reloadData() //CSVファイルにデータを保存する。 saveCSV() } //CSVファイル保存メソッド func saveCSV() { //改行区切りで部活配列を連結する。 let outputStr = dataList.joinWithSeparator("\n") do { if(outputStr == "") { //部活配列が空の場合はユーザーが保存したCSVファイルを削除する。 try fileManager.removeItemAtPath(userPath) } else { //ファイルを出力する。 try outputStr.writeToFile(userPath, atomically: false, encoding: NSUTF8StringEncoding ) } } catch { print(error) } } } |
以下は実際のプレイ動画