Swift:VaporによるSQLiteの操作(Ubuntu)
ここでは、Vapor を使って SQLite を操作する方法(CRUD)を紹介します。モジュールは FluentSQLite を使います。
SQLite3 のインストールとデータベースの作成
まず、SQLite3 をインストールします。Swift や Vapor Toolbox のインストールは、Swift:AjaxとVaporの連携(Ubuntu)を参考にしてください。
OS:Ubuntu 18.04
$ sudo apt update $ sudo apt install sqlite3
今回使用する FluentSQLite では、フィールド名(カラム名) id の整数型フィールドが必須になるので、予め次のようなテーブルを作成しておきます。
$ sqlite3 sample.sqlite sqlite> create table users (id integer, name text); sqlite> .exit
Vapor アプリケーションの作成
今回も、Vapor Toolbox を使わずに、Swift に標準搭載されている Swift Package Manager だけで作成します。
$ mkdir fluentsqlite $ cd fluentsqlite $ swift package init --type executable
続いて、Vapor Docs: Fluent Models を参考に、main.swift と Package.swift を次のように作成します。
データベースは let sqlite = try SQLiteDatabase(・・・) の所で、テーブルは User クラスの entity で設定します。
import FluentSQLite
import Vapor
var services = Services.default()
services.register(NIOServerConfig.default(hostname:"127.0.0.1", port:8080))
try services.register(FluentSQLiteProvider())
var databases = DatabasesConfig()
let sqlite = try SQLiteDatabase(storage:.file(path:"/home/tomato/sqlite/sample.sqlite"))
databases.add(database:sqlite, as:DatabaseIdentifier<SQLiteDatabase>.sqlite)
services.register(databases)
let application = try Application(config:Config.default(), environment:Environment.detect(), services:services)
final class User {
static let entity = "users"
var id:Int?
var name:String
init(id:Int? = nil, name:String) {
self.id = id
self.name = name
}
}
extension User:Content {}
extension User:SQLiteModel {}
User.defaultDatabase = DatabaseIdentifier<SQLiteDatabase>.sqlite
let router = try application.make(Router.self)
router.get("create") { request -> Future<[User]> in
let user1 = User(id:1, name:"mimi")
let _ = user1.create(on:request)
let user2 = User(id:2, name:"nana")
let _ = user2.create(on:request)
let user3 = User(id:3, name:"toto")
let _ = user3.create(on:request)
return User.query(on:request).all()
}
router.get("readAll") { request -> Future<[User]> in
return User.query(on:request).all()
}
router.get("read") { request -> Future<[User]> in
return User.query(on:request).filter(\User.name == "mimi").all()
}
router.get("update") { request -> Future<[User]> in
let user = User(id:1, name:"momo")
let _ = user.update(on:request)
return User.query(on:request).all()
}
router.get("delete") { request -> Future<[User]> in
let user = User(id:3, name:"toto")
let _ = user.delete(on:request)
return User.query(on:request).all()
}
try application.run()
注)Future<[User]> は、Future<Array<User>> のことです。
注)"let _ = ・・・" は、戻り値があるけれど、戻り値を使わない場合の表記法です。もちろん、"let _ =" を外しても構いません。
import PackageDescription
let package = Package(
name:"fluentsqlite",
dependencies:[
.package(url:"https://github.com/vapor/fluent-sqlite.git", from:"3.0.0"),
.package(url:"https://github.com/vapor/vapor.git", from:"3.1.0"),
],
targets:[
.target(name:"fluentsqlite", dependencies:["FluentSQLite", "Vapor"]),
]
)
注)Vapor Framework 及び Fluent SQLite の最新バージョンは、Vapor releases 及び Fluent SQLite releases で確認してください。
ファイルの配置は以下の通りです。
/home/tomato/fluentsqlite/Sources/fluentsqlite/main.swift /home/tomato/fluentsqlite/Package.swift /home/tomato/sqlite/sample.sqlite
Vapor アプリケーションのビルドと起動
Package.swift のあるディレクトリで以下のコマンドを実行してください。
$ swift package tools-version --set-current $ swift build $ .build/debug/fluentsqlite
ビルドに成功し、アプリケーションを起動したら、ブラウザのアドレスバーに以下のように入力してください。
http://127.0.0.1:8080/create
http://127.0.0.1:8080/readAll
http://127.0.0.1:8080/read
http://127.0.0.1:8080/update
http://127.0.0.1:8080/delete
以上が CRUD の基本的な使い方です。
次にテーブルのセルの選択の仕方です。まず、filter でレコード(行)を選択し、続いて map や flatMap でフィールド(列)を選択します。次の例は、flatMap を使って、選択したフィールドを更新して保存する例です。
router.get("filter_first") { request -> Future<User> in
let futureUser = User.query(on:request).filter(\User.id == 1).first().flatMap(to:User.self) { user -> Future<User> in
user!.name = "momo"
return user!.save(on:request)
}
return futureUser
}
router.get("filter_all") { request -> Future<User> in
let futureUser = User.query(on:request).filter(\User.id == 1).all().flatMap(to:User.self) { users -> Future<User> in
users[0].name = "momo"
return users[0].save(on:request)
}
return futureUser
}
データの更新はよくやることなので、もう少し簡潔に記述できると嬉しいんですが、Future パターンにしようとするとこういう記述になるんでしょうか。
map と flatMap の違いは、map の場合はクロージャの戻り値の型が何でもいいのに対し、flatMap の場合は戻り値の型が Future<T> だという点です。 flatMap は、Future から Future への map で、Future 型を変更しないことから、このように命名されています。
instance_of_Future<A>.flatMap(to:B.self) { a -> Future<B> in
return instance_of_Future<B>
}
以上のプログラムでは戻り値の型を明記していますが、Swift には型推論があるので適宜省略してください。
感想
Ubuntu 18.04 でもあっさり動作してくれました。 実戦で使えそうな感触を得たので、私は実戦で使います!^^
参考サイト
- Vapor Docs: SQLite
- Vapor Docs: Style Guide
- Vapor Docs: Fluent Models
- Vapor Docs: Fluent Queries
- Vapor Docs: Fluent Transactions
- Fluent Reference
- Fluent SQLite releases
- DatabaseKit Reference
- What's New In Vapor 3
- Tutorial: How to write Models using Fluent
- Swift製webアプリケーションフレームワーク Vapor を読むやつ #1
- データベースオブジェクトの命名規約