Swift転職なら=>【LevTech】
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大

頭痛が減ったので共有です!

rebuild.fmを応援しています!

HOME > SwiftData+SqliteのDBファイル保存+Libraryフォルダから読込

SwiftData+SqliteのDBファイル保存+Libraryフォルダから読込

前回の記事でSQLiteのラッパのRMDB, SWiftData,SQLite.swiftの検証の結果、SwiftDataを利用することにしました。 (前回の記事はこちらから)

さて、私の目標は事前に作成したSqliteのDBファイルをSwiftDataで読み込むことですので、その一連の作業を以下説明してまいります。

サンプル画像

以下の流れで進めていきます


1. SqliteのサンプルDBファイルを作成する

2. プロジェクトのLibraryディレクトリに保存する

3. SwiftDataからDBファイルをOpenする

4. SwiftDataでDBファイルの中身を読み込む

5. 完了

参考:IOS 8 Store sqlite File Location Core Data - Stack Overflow
参考:iOS - Xcode6のiPhone Simulatorのディレクトリが分かりにくかったけどいいアプリめっけた! - Qiita
参考:[iPhone] FMDB を使って SQLite データベースを設定する
参考:iOS開発に便利!SQLiteデータベースを手軽に編集できるツール「SQLite Database Browser」が大幅にバージョンアップしていた | Lancork
参考:今こそ復習したい、iOSアプリのディレクトリ構成 - Qiita

1. SqliteのサンプルDBファイルを作成する

まず調査した中で、SQLite Database Browserというソフトウェアが便利そうなので利用してみます。前回の 記事ではFirefoxのSQlite managerを利用していましたが、やはり使用していないブラウザなのでこちらを利用してみます。 ダウンロードはこちらから。

記事のバージョンは3.2で大幅改善と記載されていますが、現状(2016/6月)では3.6まで開発が進んでいるようです

参考:iOS開発に便利!SQLiteデータベースを手軽に編集できるツール「SQLite Database Browser」が大幅にバージョンアップしていた | Lancork
参考:Macで「開発元が未確認の為開けません」と言われて開けないアプリを開く方法

試してみる、、、

サンプル画像

えー、このSQLite Database Browserを今つかっているのですがかなり使い勝手が良いです。 直感的にDB+テーブル作成、情報のインサートがすんなりできました。FirefoxのSqlite managerより断然使い易い印象。 仕事でなれているphphmyadminよりもわかり易い第一印象です。


ただ、あれ、、、どうやってDBファイルに出力するんだろ、、、プロジェクトファイル、CSVやSQLへのエクスポートは すぐわかったんですが、肝心のDBファイルとしての出力に手間取っています。

→ (解決)ありました。というか、最初にNew Databaseした時に 作成したファイル自体がsqliteのDBファイルのようでした。拡張子が空白でしたのでわかっていませんでした。。。ここは *.sqliteみたいにしてて欲しかった。でも簡単に作成していた(簡単過ぎる)だけに灯台下暗しでした。これでファイル作成は完了です


Sqliteファイルの構成は以下の画像を参照下さい。サンプルのDBファイル自体をネット上で見つけられなかったので、 このsqliteファイルをアップロードしておきます。よろしければ自由にご利用ください!

サンプル画像


        BEGIN TRANSACTION;
CREATE TABLE "m_user" (
    `id`    INTEGER PRIMARY KEY AUTOINCREMENT,
    `name`  TEXT
);
INSERT INTO `m_user` VALUES (1,'swift salaryman');
INSERT INTO `m_user` VALUES (2,'manga salaryman');
INSERT INTO `m_user` VALUES (3,'design salaryman');
COMMIT;

swiftsalaryman.sqlite(2k)をダウンロードはこちらをクリック

2. プロジェクトのLibraryディレクトリに保存する

SwiftDataはデフォルトだと自動でDocumentフォルダにSwiftData.sqliteを作成してくれて便利なのですが、 今回私がやりたいのは「既に作成されてSQliteファイル」をLibraryフォルダから読み込むことです。 (Libraryフォルダに保存する理由は以下URLにも記載されていますが、Documentフォルダにはユーザが作成したデータを保存する 領域で《SwiftDataでユーザがデータテーブルを作成するのは問題無い》ですので、Libraryフォルダにsqliteを保存します。 開発者が事前に埋め込んだファイルがDocumentにあった場合リジェクトされる原因になるようです。) 参考:今こそ復習したい、iOSアプリのディレクトリ構成 - Qiita

まずDocumentとLibraryディレクトリを見てみましょう。


        let documentsPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as! String
        println("Document Path:"+documentsPath)
        let libraryPath = NSSearchPathForDirectoriesInDomains(.LibraryDirectory, .UserDomainMask, true)[0] as! String
        println("Library Path:"+libraryPath)
        

ここで出力されたパスを、Finder → 移動 → フォルダに移動 から、該当場所にアクセスしてみましょう

サンプル画像


するとSwiftDataを一度でも利用していればDocumentフォルダにSwiftData.sqliteが自動で作成されているのがわかります

サンプル画像


今回はこのLibraryフォルダに上で作成したswiftsalaryman.sqliteをコピーします。

サンプル画像

3. SwiftDataからDBファイルをOpenする

標準ではSwiftData.swiftはDocumentフォルダのSwiftData.sqliteにアクセスしますので、 SwiftData.sqliteの以下createPath関数を書き換えます。


        class func createPath() -> String {
            
            let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
            let databaseStr = "SwiftData.sqlite"
            let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
            
            return dbPath
        }


        class func createPath() -> String {
            
            let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibrarytDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
            let databaseStr = "swiftsalaryman.sqlite"
            let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
            
            return dbPath
        }

これでswiftsalaryman.sqliteをオープンします

4. SwiftDataでDBファイルの中身を読み込む

かなり簡単でした。上記のcreatePathさえ設定しておけば、 読み出すのは以下のexecuteQueryだけでOK。データベースをOpenする記述も必要なし(内部的に実行してくれている)


       var result = NSMutableArray()
        let (resultSet, err) = SD.executeQuery("SELECT * FROM m_user ORDER BY id DESC")
        if err != nil {
            //エラー処理
        } else {
            for row in resultSet {
                if let id = row["id"]?.asInt() {
                    let nameStr = row["name"]?.asString()!
                    let dic = ["id":id, "name":nameStr!]
                    result.addObject(dic)
                }
           
            }
        }
        println(result.description);
        

以下、出力結果です。バッチリ取得できていました!


(
        {
        id = 3;
        name = "design salaryman";
    },
        {
        id = 2;
        name = "manga salaryman";
    },
        {
        id = 1;
        name = "swift salaryman";
    }
)

テーブルが存在するかを確認したり、テーブルを作成したり、Insert等のSQL処理も簡単にできますが、 今回の目標は達成できましたのでここで完了します。

既に存在するSqliteのデータベースにアクセスする方法(追記2017/07/10)

さて上記のコードではエミュレータ上で試すには問題なかったのですが、端末でテストをすると以下エラーが


SwiftData Error -> During: SQL Prepare
                -> Code: 1 - SQL error or missing database
                -> Details: no such table: m_content

                

m_contentは僕の自作のテーブル名ですのでこの箇所は可変です。

なんのエラーか調べると、どうやらLibraryフォルダにsqliteファイルが存在しない。。。。当たり前か、、、 プロジェクトに追加せずに直接Libraryフォルダに入れていたので納得です。ただ、LibraryフォルダにProjectから 直接配置することができませんので、XCodeのプロジェクトへドラッグで追加した上、Build PhaseでBundle(Copy Bundle Resourece)に含めた上で、 Libraryフォルダにsqliteファイルをコピーする処理を実装する必要があります。

サンプル画像

処理の方法は以下で解説されていましたので共有します。

参考:How to use existing database #4


        class func createPath() -> String {
            
            let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibrarytDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
            let databaseStr = "swiftsalaryman.sqlite"
            let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
            
            return dbPath
        }


        class func createPath() -> String {
            
            let docsPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.LibrarytDirectory, NSSearchPathDomainMask.UserDomainMask, true)[0] as! String
            let databaseStr = "swiftsalaryman.sqlite"
            let dbPath = docsPath.stringByAppendingPathComponent(databaseStr)
            
                // BEGING MODIFICATION
                let fileMan = NSFileManager.defaultManager()
                if !(fileMan.fileExistsAtPath(dbPath)) {    // The database does not already exist in Documents directory
                    if let source = NSBundle.mainBundle().resourcePath?.stringByAppendingPathComponent(databaseStr) {
                        if !(fileMan.fileExistsAtPath(source)) {
                            println("SQLiteDB - file \(databaseStr) not found in bundle")
                        } else {
                            var error:NSError?
        
                            if !(fileMan.copyItemAtPath(source, toPath: dbPath, error: &error)) {
                                println("SQLiteDB - failed to copy writable version of DB!")
                                println("Error - \(error!.localizedDescription)")
                            }
                        }
                    }
                }
                // END MODIFICATION

         return dbPath
        }

指定の場所にファイルがなければBundleからコピーします。との内容です。すでに端末で試している人は空のSqliteファイルが SwiftDataによって作成されているかもしれませんので、一度端末のアプリを削除した上で実行してみてください。

まとめ

これでSqliteをSwiftで使う目処がつきました。何かヘルプになっていれば幸いです。

前の記事はこちらからです!

次の記事はこちらからです!

↓こんな記事もありますよ!

NSThreadでスレッド処理を行う

タイマー処理と同様に利用できるスレッドの処理です。NSTimerではヒゲがのびたサンプルでしたので眉毛の濃くなるサンプルにしてみます。ただ、どうやらThread処理は 推奨されておらず、dispatch_queueを使用するべきのようです。

RSSリーダで利用するAPIを検証してみる

ニュースアプリの様なRSSを表示させたいのですがXMLを処理するAPIが数点存在するようです。 Swiftにあったものを見つけ出せたらと思います。

UITabBarItem

タブバーを利用した時にアイコンがデフォルトのものだけでは物足りなかったのでフリー素材でよさそうなサイトがあったので共有です。 500個も魅力的でオーソドックスで使いやすいアイコン盛りたくさん。
このエントリーをはてなブックマークに追加
右側のFacebookのLikeをクリック頂けると記事更新の際に通知されますので宜しければご利用下さい!