ゲームプログラム、swift3.0でCSVファイルを扱うのでSwiftCSVライブラリをゲットしたがエラーでまくり

swift3.0でCSVファイルを扱うのでSwiftCSVライブラリをゲットしたがエラーでまくり

色々エラー

https://github.com/naoty/SwiftCSV
良かったライブラリあるじゃん、楽できるじゃんと思ったが、
でもSwiftのバージョン3.0に上がったためにエラー20件ほど。チクチク直すが、自分で基本的なところだけでも作ったほうが良くないか?と思いはじめて、簡単読み込みを作り直すことにする

他にもあったけど、Swift3.0だと記述が変わってエラー出まくりで対応できず。
バージョンが上がるとかなりしんどくなるので、開発中に、バージョンを上げるのは今後は難しくなる。アップルらしからぬ上位互換無視なので、これからは古い環境も必要となるのでしょう。
No calls to throwing functions occur within ‘try’ expression
init has been renamed

よくわからないが、次のように書き直した。ちなみに、このコードの上にも同じコードがある
let contents = try String(contentsOfURL: url, encoding: encoding)

let contents = try String(describing: (contentsOfURL: url, encoding: encoding))

ただし、書き直しても警告がでてる。

http://qiita.com/mono0926/items/88779ceff30f8fc705c5
より
ndexがsuccessor()・predecessor()・advancedBy(_:)・advancedBy(_:limit:)・distanceTo(_:)でエラーになったときの書き換え

引用
Swift3.0 の
let str = “ABC”
let idx0 = str.startIndex
let idx1 = str.index(after: idx0)
let idx2 = str.index(after: idx1)
str[idx0] // => ‘A’ Character型
str[idx1] // => ‘B’ Character型
str[idx2] // => ‘C’ Character型

Realmを使うとに知っておくと良い情報

Realmファイルが保存されている場所をデバッグプリントしてくれます。

print(Realm.Configuration.defaultConfiguration.fileURL!)

CSVデータをエクセルで作成して、RealmDBに挿入するところをやってみました。

本当は、基本的な部分、いわゆるテーブルを作成して、RealmBrowserでインサートしようとしたのですが、Realmブラウザーがクラッシュしてしまい使えず。
仕方なく、CSVファイルを読みこむコードを書きました。
このCSVファイルの読み込みについてもネット探して、ライブラリを見つけたんですが、Swift2.0で書かれたたみたいで、Siwft3.0でコンパイルしようとするとエラーがてんこもりで諦めて、
ネットの情報を参考にして、RealmDBに登録しました。

参考にしたサイトは

http://hajihaji-lemon.com/smartphone/swift/csv/
こちらはテーブルビューに登録して、出力についても記述ありです。

http://qiita.com/star__hoshi/items/90c9c54301027b01ffa2
Realmの情報です。残念ながら私の環境では動作しませんでしたが、クラスの書き方が参考になります

http://qiita.com/aikawa_Japan/items/ce4a05e78aa23449fa1e
CSV読み込みの部分のみ

この3つを参考にして、引用しています。

ちなみに読み込んだCSVの行数は
304行
3〜4列で、全てが4列揃っていない情報です。

こんな感じです。
1,run,走る,
2,eat,食べる,
3,see,見る,テスト

サンプルコード 以下のコードはCSVを読み込みRealmDBに登録するまでを目的としています。テーブルビューに表示させる内容ではありません。
Realmファイルを初期データとして利用するために作ったコードです。

出力したCSVはドラッグしてプロジェクトに取り込みました。

このDBからまた、配列に取り込む作業を行います。
数百行程度ならたぶんDBは不要なのでしょうけど、検索処理があるなら、DBを使ったほうが扱いやすいと思います。
ただ、SQLとかあまり使わない人であれば、プログラムのコードを書いたほうが楽かもしれません。
RealmはDBといっても、SQLのSELECT文みたいな感じではないので、とっつきにくい感じがします。

CSV読み込み、サンプルコード

CSVファイル例

1,run,走る,
2,eat,食べる,
3,see,見る,
4,spasm,発作,
5,remainsruins,遺跡,
6,inheritance,相続,
7,harbor,かくまう,
8,jeers,罵声,
9,interrogate,尋問,
10,plunge,突入,
11,persuade,説得,パースエイド:パースがえーどと説得する
まだ続く

RealmDBのテーブルオブジェクトの構成

サンプルコード

以下のコードは単に、Realmを使うだけで、ViewやSceneには表示させていません。Realmファイルを生成するのが目的です。

CSVファイルから読み込んでRealmDBに登録したtestRealm2プロジェクトのGameSceneのコード

import SpriteKit
import GameplayKit
import RealmSwift
/*
 手順CSVファイルを読み込み
 RealmDBファイルを作成する。Etango
 このファイルを、初期データとして使用する。
 そのためCSVの読み込みは、完成プロジェクトでは不要となる。
 これは、完成プロジェクトのための事前準備プロジェクトファイルである。
 そして、Seisekiクラスで、英単語の成績を記録します。
 この成績は、Documentに保存されます。
 そして、Etangoクラスで作成したpokemon2.relmは読み込み用DBはファイルとなります。
 なので、完成プロジェクトでは、読み込み用のDBと書き込み用のDBが存在します。
 よって、このプロジェクトはとりあえず、読み込み用のDBを作成するだけのプロジェクトとします。
 
 */
class GameScene: SKScene {
    
    private var label : SKLabelNode?
    private var spinnyNode : SKShapeNode?
    
    override func didMove(to view: SKView) {
        //CSV
        var dataList:[String] = []
        do {
            //CSVファイルのパスを取得する。
            let csvPath = Bundle.main.path(forResource: "test", ofType: "csv")
            
            //CSVファイルのデータを取得する。
            let csvData = try String(contentsOfFile:csvPath!, encoding:String.Encoding.utf8)
            
            //改行区切りでデータを分割して配列に格納する。
            dataList = csvData.components(separatedBy: .newlines)//componentsSeparatedByString("\n") is notwork
            
        } catch {
            print(error)
        }
        
        //INSERT Realmが必要
        let realm = try! Realm()
        let fileManager = FileManager()
        let pokemonRealmPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/pokemon2.realm"
        
        // 初期データを作成するため、前回作成したデータがあったら削除する
        if fileManager.fileExists(atPath: pokemonRealmPath) { try! fileManager.removeItem(atPath: pokemonRealmPath) }
        try! realm.write { realm.deleteAll() }
        print(Realm.Configuration.defaultConfiguration.fileURL!)
        
        //let dataDetail = dataList[indexPath.row].componentsSeparatedByString(",")
        for i in dataList{
            let row = i.components(separatedBy: ",")
            let cEtango = Etango()//realm object いわゆるテーブルにあたる。id,etango,japanese,goro,count,etcの5列
            let id0 = row.startIndex //0
            let ide = row.endIndex
            print ("id0 = \(id0)")
            print ("ide = \(ide)")
            print ("start-while")
            var idx:Int!
            idx = id0
            while idx < ide{//配列の数(カラム列数)だけループします。
                if row[id0] == ""{
                    
                }else{
                    
                    print ("idx = \(idx)")
                    switch idx {//Switchに分けているのはout of rangeエラー、配列の中身が無いのに、空っぽの中身を取得しようとしてエラーになるのを防ぐ。
                    case 0:
                        cEtango.id = Int(row[0])!
                        print ("row[0] = \(Int(row[0]))")
                        break
                    case 1:
                        cEtango.etango = row[1]
                        //
                        break
                    case 2:
                        cEtango.japanese = row[2]
                        break;
                    case 3:
                        if row[3] != ""{
                            cEtango.goro = row[3]
                        }
                        break;
                    case 4:
                        if row[4] != ""{
                            cEtango.count = row[4]}
                        break;
                    case 5:
                        if row[5] != ""{
                            cEtango.etc = row[5]
                        }
                        break;
                        
                    default:
                        break;
                    }//switch
                }//if
                
                idx = row.index(after: idx)
            }//while
            try! realm.write {
                realm.add(cEtango)
            }//try
        }
        
        try! Realm().writeCopy(toFile: URL(string: pokemonRealmPath)!, encryptionKey: Data(base64Encoded: "pokemon"))
        
        //file:///Users/maseda/Library/Developer/CoreSimulator/Devices/66E1992E-5A1A-40A6-8CED-407B613A6ADB/data/Containers/Data/Application/948741DF-98F9-4EFC-8A61-53DE091F5933/Documents/default.realm
        
        
    }
}

testRealm3のGameSceneのコード
pokemon2.realmからテスト的に読み込みをして、成績管理用のSeiseki.realmを作成するコード
testRealm2のプロジェクトとわざわざ分けて作成する必要はないのですが、あとでコードを確認するときに、分けておいたほうがコードを実行しやすいのと、見やすいと思ったから。

/*
 pokemon2.realmを作成したのは、testRealm2プロジェクトです。
 このtestRealm3プロジェクトは、DB読み込みとSeisekiクラスを使って成績データをDBに記録します。
 実験用プロジェクトです。
 では、どうやって動作確認をするか?
 まず、成績用ダミーデータを保存できるかを確認します。
 つぎに、その成績データを読み込みできるかを確認します。
 そして、その成績データに基づいて、読み込み用DBのデータ、実際には、その読み込み用DBから取得した配列変数を操作することになるでしょう。
 
 こればかりすると、きっと飽きてくるので、途中でUnityで遊んだほうが良さそうです。
 */
import SpriteKit
import GameplayKit
import RealmSwift

class GameScene: SKScene {
    
    private var label : SKLabelNode?
    private var spinnyNode : SKShapeNode?
    
    override func didMove(to view: SKView) {
        //pokemon2.realmの読み込み
        var config = Realm.Configuration()
        let path = Bundle.main.path(forResource: "pokemon2", ofType: "realm")
        config.fileURL = URL(string: path!)
        config.readOnly = true
        Realm.Configuration.defaultConfiguration = config
        print (try! Realm().objects(Etango.self).first?.etango as Any )
        print (try! Realm().objects(Etango.self).last?.id as Any )
        config.readOnly = false
        Realm.Configuration.defaultConfiguration = config
        //閉じる、変更
        config = Realm.Configuration()
        config.readOnly = false
        Realm.Configuration.defaultConfiguration = config
        //成績用DBの作成
        let realm = try! Realm()
        let fileManager = FileManager()
        let pokemonRealmPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/seiseki.realm"
        // 初期データを作成するため、前回作成したデータがあったら削除する
        if fileManager.fileExists(atPath: pokemonRealmPath) { try! fileManager.removeItem(atPath: pokemonRealmPath) }
        try! realm.write { realm.deleteAll() }
        print(Realm.Configuration.defaultConfiguration.fileURL!)
        
        //seiseki.realmへの書き込み
        /*
         実際の処理は、正解したときに、SeisekiDBに書き込み処理を行います。
         まず、検索して、Updateになります。IDは、加算方式になります。Countも加算です。
         */
        
        let cSeiseki = Seiseki()
            cSeiseki.id = 1
            cSeiseki.etango = "run"//try! Realm().objects(Etango.self).last!.etango
        
            //try! Realm().objects(Etango.self).first?.etango as Any as! String
                /*
                 let realm = try! Realm()
                 let user = realm.objects(RealmDB.self).last!
                 */
            cSeiseki.count = 1
            cSeiseki.update_date = String(describing: NSDate())
        
        try! realm.write {
            realm.add(cSeiseki)}//try
        //copy
        try! Realm().writeCopy(toFile: URL(string: pokemonRealmPath)!, encryptionKey: Data(base64Encoded: "seiseki"))
    }
    
   
}

RealmDBクラス
プライマリーキーはetangoカラムです。SQLでいうところのテーブルを作成するところです。

import Foundation
import RealmSwift

class Etango: Object {
    
    dynamic var id:Int = 0
    dynamic var etango = ""
    dynamic var japanese = ""
    dynamic var goro	= ""
    dynamic var count = ""
    dynamic var etc = ""
    
    override class func primaryKey() -> String {
        return "etango"
    }
}

成績管理用クラス
プライマリーキーはetangoカラムです。

import Foundation
import RealmSwift
class Seiseki: Object {
    
// Specify properties to ignore (Realm won't persist these)
    
//  override static func ignoredProperties() -> [String] {
//    return []
//  }
    dynamic var id:Int = 0
    dynamic var etango = ""
    dynamic var count:Int = 0
    dynamic var update_date = ""
    override class func primaryKey() -> String{
        return "etango"
    }
}

全体作業の手順の説明

(1)事前準備Realmライブラリをプロジェクトに登録する。

英単語データ作成
英単語と意味と語呂合わせなどのデータをエクセルdw作成

csv出力
csvファイルをXcodeプロジェクトに読み込み、ドラッグアンドドロップ

Swiftにてcsv読み込みとRealmDBオブジェクト作成インサートしてRealmファイルを作成
ここまで読み込み用プロジェクトとした。これは後で参考にする時に全部一緒だと実行して確認するのに手間だから。
(2)
別のプロジェクトを作成してRealmライブラリを登録
プロジェクトにRealmファイルをドラッグアンドドロップする。

Realmファイルから配列に登録

配列から問題出題

正誤を成績テーブルオブジェクトに登録

ここまで見るとなんとも面倒な作業をしています。最初からcsvを配列に入れて、使えば良いわけで、わざわざRealmDBやRealmファイルを使う必要はなさそうです。
そうなんですが、最終的に登録数や検索内容が多くなるとDBを使う方が便利になりそうなので勉強も兼ねて、RealmDBを使用しています。今は300行程度ですが目標が3000語です。
3000語ともなるとエクセルで扱うにも面倒な数です。量が増えても良いように今のうちに設計しておこうと思います。

こちらの記事もどうぞ