見出し画像

【徒然iOS】気ままにUIKit108〜CSVファイルを操作してみよう〜

概要

このマガジンは四十を過ぎたおっさんが、

を参考にStoryboardでiOSアプリを完全に趣味で楽しんでいるだけな記事を気ままに上げてます。

今回

をハイ、レッツゴ🕺

前準備

念の為、

  1. バックアップ

  2. 新規クラスを追加

  3. 新規ビューを追加

をいつもどおりやってから本題へ💃

こんな感じかな💦

本題

CSVファイルとは、

Comma Separated Valuesの略
👉データをカンマ区切りで並べたファイル
Tab区切りのモノをTSVって言うねえ🤔
なぜか、それも含めてCSVってゆー人も多いけど、、、💦

データベース(DB)の何らかの操作を経験したことがある人は一度は、

.csv

て拡張子のファイルに触れたことが多いはず。
そう、お馴染みのアレです🕺

データ永続化の観点ではCore Dataでも同じことができるが、

CSVファイルのほうが手軽に編集できる

他のアプリにデータを渡すときもCSVファイル1つを渡すだけでいい
👉取り扱いが楽

ってことらしい🤔

    //一覧
    let heianSisters = [
        [1, "紫式部", "源氏物語"] ,
        [2, "清少納言", "枕草子"] ,
        [3, "和泉式部", "和泉式部日記"]
    ]

のデータを今回は使って〜〜〜〜

事前準備

まずは、とりあえず、、、

テーブルを配置して〜〜〜
テーブルをビュー全体にして、テーブルセルを配置〜〜〜
テーブルをビューにデータソースとデリゲートでそれぞれ接続〜〜〜
配置したテーブルセルのIdentifierに名前をつけて〜〜〜
StyleをRightDetailに〜〜〜
テーブルビューをアウトレット接続〜〜〜
てな感じでラベルをふたつ配置して

今回のコード(事前準備)

class CSVViewController: UIViewController,UITableViewDataSource {
    @IBOutlet weak var myTableView: UITableView!
    //一覧
    let heianSisters = [
        [1, "紫式部", "源氏物語"] as [Any],
        [2, "清少納言", "枕草子"] as [Any],
        [3, "和泉式部", "和泉式部日記"] as [Any]
    ]
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    //データを返すメソッド
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //セルを取得する。
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableCell", for:indexPath) as UITableViewCell
        //セルのラベルに作者名を設定する。
        cell.textLabel?.text = heianSisters[indexPath.row][2] as? String
        cell.detailTextLabel?.text = heianSisters[indexPath.row][1] as? String
        return cell
    }
    //データの個数を返すメソッド
    func tableView(_ tableView:UITableView, numberOfRowsInSection section:Int) -> Int {
        return heianSisters.count
    }
}

を組み込んで〜〜〜

出ました。平安シスターズ

うう、ここまで準備するのに1時間くらいかかったよ〜〜〜😭
以下は、大見出しで〜〜〜

CSVから入力

⒈CSVファイルから読み込むように変更

てな感じで選んで〜〜〜
コイツを選んで〜〜
名前を入れて〜〜〜
ハイ、ファイルできた〜〜〜

⒉編集欄にCSVのデータを入力

1,紫式部,源氏物語
2,清少納言,枕草子
3,和泉式部,和泉式部日記

てな感じで入力

⒊コード組み込み

//
// 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取り込み)

class CSVViewController: UIViewController,UITableViewDataSource {
    @IBOutlet weak var myTableView: UITableView!
    //一覧
    var heianSisters:[String] = []
    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            //CSVファイルのパスを取得する。
            let csvPath = Bundle.main.path(forResource: "heianSisters", ofType: "csv")
            //CSVファイルのデータを取得する。
            let csvData = try String(contentsOfFile:csvPath!, encoding:String.Encoding.utf8)
            let lineChange = csvData.replacingOccurrences(of: "\r", with: "\n")
            heianSisters  = lineChange.components(separatedBy: "\n")
            heianSisters.removeLast()
        } catch {
            print(error)
        }
    }
    //データを返すメソッド
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //セルを取得する。
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableCell", for:indexPath) as UITableViewCell
        //カンマでデータを分割して配列に格納する。
        let dataDetail = heianSisters[indexPath.row].components(separatedBy: ",")
        //セルのラベルに作者名を設定する。
        cell.textLabel?.text = dataDetail[2]
        cell.detailTextLabel?.text = String(dataDetail[1])
        return cell
    }
    //データの個数を返すメソッド
    func tableView(_ tableView:UITableView, numberOfRowsInSection section:Int) -> Int {
        return heianSisters.count
    }
}

⒋シミュレータで実行

変わってないねえ〜〜〜
って当たり前か🕺
動いて、表示できた=問題なく取り込めたってことで💦

CSVファイルを出力(ディレクトリが怪しい)

⒈コード組み込み

//
// 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)
        }
    }
}

を参考に

class CSVViewController: UIViewController,UITableViewDataSource {
    @IBOutlet weak var myTableView: UITableView!
    //一覧
    var heianSisters:[String] = []
    //CSVファイルの保存先
    var userPath:String!
    let fileManager = FileManager()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        do {
            //ユーザーが保存したCSVファイルのパス
            userPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/heianSisters.csv"
            var path = userPath
            if(fileManager.fileExists(atPath: path!) == false){
                //ユーザーが保存したCSVファイルが無い場合は、初期CSVファイルから読み込む。
                path = Bundle.main.path(forResource: "/heianSisters", ofType: "csv")
            }
            //CSVファイルのデータを取得する。
            let csvData = try String(contentsOfFile:path!, encoding:String.Encoding.utf8)
            //改行区切りでデータを分割して配列に格納する。
            let lineChange = csvData.replacingOccurrences(of: "\r", with: "\n")
            heianSisters  = lineChange.components(separatedBy: "\n")
            //テーブルビューを編集モードにする。
            myTableView.isEditing = true
            //CSVファイルの出力先を確認する。
            print(userPath as Any)
        } catch {
            print(error)
        }
    }
    //データを返すメソッド
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        //セルを取得する。
        let cell = tableView.dequeueReusableCell(withIdentifier: "MyTableCell", for:indexPath) as UITableViewCell
        //カンマでデータを分割して配列に格納する。
        let dataDetail = heianSisters[indexPath.row].components(separatedBy: ",")
        //セルのラベルに著者名を設定する。
        cell.textLabel?.text = dataDetail[2]
        cell.detailTextLabel?.text = String(dataDetail[1])
        return cell
    }
    //データの個数を返すメソッド
    func tableView(_ tableView:UITableView, numberOfRowsInSection section:Int) -> Int {
        return heianSisters.count
    }
    //テーブルビュー編集時に呼ばれるメソッド
    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        //削除の場合、配列からデータを削除する。
        if( editingStyle == UITableViewCell.EditingStyle.delete) {
            heianSisters.remove(at: indexPath.row)
        }
        //テーブルの再読み込み
        tableView.reloadData()
        //CSVファイルにデータを保存する。
        saveCSV()
    }
    //CSVファイル保存メソッド
    func saveCSV() {
        //改行区切りで部活配列を連結する。
        let outputStr = heianSisters.joined(separator: "\n")
        do {
            if(outputStr == "") {
                //部活配列が空の場合はユーザーが保存したCSVファイルを削除する。
                try fileManager.removeItem(atPath: userPath)
            } else {
                //ファイルを出力する。
                try outputStr.write(toFile: userPath, atomically: false, encoding: String.Encoding.utf8)
            }
        } catch {
            print(error)
        }
    }
}

で書き換えたんだけど、

⒉シミュレータで実行

すると、

な感じで、データはあるのにIndex out of rangeになる👀
エラー箇所をコメントアウトして実行すると、
てな感じで機能的には出来上がってる様子🤔

データを見ても、おかしなところはなさそうなんだよね〜〜〜
ちょっと、わからないなあ。

てか、できたとしても正直、

ドキュメントフォルダ内のCSVファイルを直接触りに行って、
全部削除出来たら、ファイルごと削除

なんて機能、危険すぎて使わないし。
個人的には入れないなあ🤔

よりかは、安全に必要なモノをアプリ内で編集させて、
その後に出力→上書きの方が安全でしょ👀

てことで、サイト記事の内容については以上🕺

ブラッシュアップ

このままだと、メニューでCSVボタンをタップしても次のビューに進めないままなので、CSV取り込みのコードに戻してから、

今回も、地球ボタンだけ追加して〜〜〜

てな感じで

記事公開後、

ハイ、完了💃
実機も問題なし🕺

Apple公式

以上。

いやあ〜〜〜長かった〜〜〜〜💦💦
合計108記事
人間の煩悩の数と同じだね👀
💃除夜の鐘〜〜〜🕺

さてと、

ちょっと作りたいアプリがあるので、来週からは、完全に

SwiftUI

に戻ろう🕺

個人的には、

💃UIKitの良い振り返りになった🕺

6年前以上前のサイト記事を参考に、4年ぶりくらいに作ってみたけど、

正直、ここまでまだ全然動くとは思わなかったわ〜〜〜!
サイトの記事を参考にはしたけど、
コードの書き方自体も変わってるし、
全てのビューをひとつのファイルにまとめてるから、
もはや完全にオリジナルなモノになってしまった💦💦

全ての標準機能のビューを、まとめてもこれくらいだね👀

とまた、話が長くなりそうだし、

あとの感想は、また後日別記事で書こう🕺


この記事が気に入ったらサポートをしてみませんか?