Vaporでp-x9/AliasMacroをContentやModelに使ってみる。
Last Updated on 2024年1月21日 by lemonade
@Mさん(Twitterアカウント)が作ったSwift Macro、AliasMacroという関数や変数の名前に日本語等のエイリアス名を作るマクロを使用して、VaporのORMであるFluent ModelやJSONに変換できるCodableな型、Contentに使用してみた結果を書いてみます。
環境
- MacBook Pro 14inch M1 Pro
- macOS Sonoma 14.2.1
- Swift 5.9.2
- Vapor Framework 4.91.1
- Vapor toolbox 18.7.4
セットアップ
Swift Package ManagerでAliasMacroの依存を追加する
Package.swiftのdependenciesに.package(url: "https://github.com/p-x9/AliasMacro.git", from: "0.6.0"),
と.product(name: "Alias", package: "AliasMacro"),
を追加します。
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "alias",
platforms: [
.macOS(.v13)
],
dependencies: [
// 💧 A server-side Swift web framework.
.package(url: "https://github.com/vapor/vapor.git", from: "4.89.0"),
// 🗄 An ORM for SQL and NoSQL databases.
.package(url: "https://github.com/vapor/fluent.git", from: "4.8.0"),
// 🐘 Fluent driver for Postgres.
.package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.7.2"),
// Alias Macro
.package(url: "https://github.com/p-x9/AliasMacro.git", from: "0.6.0"),
],
targets: [
.executableTarget(
name: "App",
dependencies: [
.product(name: "Fluent", package: "fluent"),
.product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
.product(name: "Vapor", package: "vapor"),
// Alias Macro
.product(name: "Alias", package: "AliasMacro"),
]
),
.testTarget(
name: "AppTests",
dependencies: [
.target(name: "App"),
.product(name: "XCTVapor", package: "vapor"),
// Workaround for https://github.com/apple/swift-package-manager/issues/6940
.product(name: "Vapor", package: "vapor"),
.product(name: "Fluent", package: "Fluent"),
.product(name: "FluentPostgresDriver", package: "fluent-postgres-driver"),
]),
]
)
Aliasで変数名を日本語にする
Todoモデルにエイリアスをつける
import Alias
import Fluent
import Vapor
final class Todo: Model, Content {
static let schema = "todos"
@ID
var id: UUID?
@Alias("カテゴリ")
@Parent(key: "category_id")
var category: Category
@Alias("タイトル")
@Field(key: "title")
var title: String
@Alias("メモ")
@Field(key: "note")
var note: String
@Alias("状態")
@Enum(key: "state")
var state: 状態
@Timestamp(key: "created_at", on: .create)
var createdAt: Date?
@Timestamp(key: "updated_at", on: .update)
var updatedAt: Date?
init() {}
init(id: UUID? = nil, categoryId: Category.IDValue, title: String, note: String, state: State) {
self.id = id
self.$category.id = categoryId
self.title = title
self.note = note
self.state = state
}
}
extension Todo {
@Alias("状態")
enum State: String, Hashable, Content, CaseIterable {
@Alias("未完了")
case todo
@Alias("進行中")
case doing
@Alias("完了")
case done
}
}
Controllerで日本語エイリアスを使ってみる
TodoControllerのupdateメソッドを日本語を使ってみる
/// Todoを変更する
func update(req: Request) async throws -> HTTPStatus {
let id = try req.parameters.require("id", as: UUID.self)
guard let todo = try await Todo.find(id, on: req.db) else {
throw Abort(.ok, reason: "The todo does not exist")
}
try EditTodoJSON.validate(content: req)
let content = try req.content.decode(EditTodoJSON.self)
guard let category = try await Category.find(content.categoryId, on: req.db) else {
throw Abort(.ok, reason: "The category does not exist")
}
todo.$category.id = try category.requireID()
todo.タイトル = content.title
todo.メモ = content.note
todo.状態 = content.state
try await todo.update(on: req.db)
return .noContent
}
結果
AliasMacroはグローバルなクラスに対してつけるとエラーが出る。
READMEのClass/Struct/Enum/Actorにある通り、Warning PeerMacro
with arbitrary specified in names
cannot be used in global scope. でエラーが出るためモデル名に日本語でエイリアスをつけるのは難しそうです。ただ、Todo.Stateなどネストした定義のものであればエイリアスを貼ることはできました。
PropertyWrapperにはエイリアスが貼られない
todo.$category.id = try category.requireID()
などもtodo.$カテゴリ.id = try category.requireID()
とできないか試してみましたが、PropertyWrapperにはエイリアスが貼られないようです。
EncodeされたJSONにはエイリアスは関係ない
APIから返却されたJSONを見てみましたが、日本語エイリアスは反映されていませんでした。encodeする際にエイリアスは干渉しないようです。
感想
JSON値やORMに日本語エイリアスが干渉しないのがわかり嬉しかったですが、PropertyWrapperに日本語が貼られないとFluent ORMでプロパティに日本語エイリアスを貼る価値は薄いなとも思いました。SwiftUIでもPropertyWrapperに日本語エイリアスは欲しいでしょうし、PropertyWrapperに対応されれば最高ですね。