はじめに
WWDC2019で発表され、大いに注目を集めるSwiftUIですが、そのSwiftUIでWebアプリを作れることをご存知でしょうか?
SwiftWebUIを使えばできます!
SwiftWebUIは、そのままですが、SwiftUIをWebでも使えるようにしようというプロジェクトです。
今回は簡単にTODOアプリを作っていきます。
参考リンク
- GitHub - SwiftWebUI/SwiftWebUI: A demo implementation of SwiftUI for the Web
- SwiftWasm - compile Swift to WebAssembly
- iOSアプリ開発にSwift Package Managerを使おう - Qiita
- GitHub - mtfum/SwiftWebUISample
Hello World
Xcodeを開き、New Project, macOS, Command Line Toolを選択します。
適当に名前を入力し、作成できたらSwift Package ManagerでSwiftWebUIを入れていきます。
[iOSアプリ開発にSwift Package Managerを使おう - Qiita]のリンクがわかりやすいです。
きちんと導入できれば、main.swift
で下記をコピペしてBuild&Runして、http://localhost:1337にアクセスすれば、テキストの内容(Hello World
)が表示されているでしょう
import SwiftWebUI
SwiftWebUI.serve(Text("Hello World"))
続いて簡単なTODOアプリを作っていきましょう
TODOアプリ
モデルを定義します。
struct Task: Identifiable {
var text: String
var createdAt: Date
var isFinished: Bool
var id: String { text }
var createdAtString: String {
let f = DateFormatter()
f.timeStyle = .short
f.dateStyle = .short
f.locale = Locale(identifier: "ja_JP")
return f.string(from: createdAt)
}
}
タスク一つ一つのUIを作成します。
Viewが一つのtaskを持っていて、Doneボタンを押せばisFinished
がtrue
になってタスクが完了し、文字色やViewのEnabledが制御されています。
struct TaskRow: View {
@State var task: Task
var body: some View {
HStack {
Button("Done") {
self.task.isFinished = true
}
VStack(alignment: .leading) {
Text(task.name)
.font(.headline)
.color(task.isFinished ? .secondary : .primary)
Text(task.createdAtString)
.color(.secondary)
.font(.subheadline)
}
}
.padding()
.disabled(task.isFinished)
}
}
上記のUIは↓のようになります。
モデルとUIができればモックが作れるので、main.swift
を書き換えていきましょう。
import Foundation
import SwiftWebUI
struct MainView: View {
@State private var tasks: [Task] = [
Task(name: "Sample Task", createdAt: Date(), isFinished: false),
Task(name: "Sample Task", createdAt: Date(), isFinished: false),
Task(name: "Sample Task", createdAt: Date(), isFinished: false)
]
var body: some View {
Form {
Section(header: Text("Task App").font(.title)) {
List(tasks.identified(by: \.id)) { TaskRow(task: $0) }
}
}
}
}
SwiftWebUI.serve(MainView())
この状態でBuild&Runすればタスクが画面の真ん中に表示されているかと思います。
あとはタスクを追加できるようにすれば完了です。
TaskInputView
を作成し、textをTextField
で変更していき、追加ボタンを押したらactionを呼ぶようにします。
import SwiftWebUI
struct TaskInputView: View {
@State private var text: String = ""
let action: (String) -> Void
var body: some View {
HStack {
TextField($text)
Spacer()
Button("追加", action: {
self.action(self.text)
self.text = ""
})
.disabled(text.isEmpty)
}
}
}
一方で、main.swift
ではTaskInputView
のactionでString型受け取るので、tasksの配列に追加していくようにしておきます。
var body: some View {
Form {
Section(header: Text("Task App").font(.title)) {
TaskInputView(action: { text in
let task = Task(text: text, createdAt: Date(), isFinished: false)
self.tasks.append(task)
})
Spacer(minLength: 12)
Text("Task List")
.font(.title)
.bold()
List(tasks.identified(by: \.id)) { TaskRow(task: $0) }
}
}
}
下記のようなアプリが出来上がります。
既知のバグとして
- TextField内のtextの変更を受け取れていない
- Task Listを空配列で定義するとタスクを追加できない
が残っておりいろいろ雑ですが、現状のSwiftWebUIの仕様など、詳しくみれていないので時間があれば調査したいと思います。
完成形のプロジェクトをGitHubに上げておきます。
GitHub - mtfum/SwiftWebUISample
懸念点
-
NavigationView
が利用できない - Buttonの色が変更されない
- タスクの空配列に追加してもListに反映されない
-
TypeError: Cannot read property 'parentNode' of Null
のエラーが表示される
-
など、実際に触ってみるとバグも多く、利用していくにはまだまだ先になりそうです。
また、ブラウザ上でSwiftを動かすためにはSwift Wasmの対応も待たなければなりません。
最後に
SwiftWebUIはプロダクション用ではなくSwiftUIを学ぶのに適していると注意書きされています。
Disclaimer: This is a toy project! Do not use for production. Use it to learn more about SwiftUI and its inner workings.
しかし、SwiftでWebアプリがつくれる日が近づいている証でもあり、まだまだ先は長そうですが今からとても楽しみです。
SwiftUIを触ってみるには良い環境かもしれないので、興味のある人は触ってみてください。
ということでSwiftWebUIの紹介を示させてもらえればと思います。
最後までお読みいただきありがとうございました。