SwiftUI x SwiftData でシンプルなメモアプリ(データの永続化を含む)を作成する
SwiftUI x SwiftData でシンプルなメモアプリ(データの永続化を含む)を作成する手順をまとめます。
概要
SwiftUIでアプリ開発を行う上でデータの永続化が必要になったので、SwiftData を使った簡単なメモアプリをサンプル実装することで使い方をまとめておきます。
SwiftUIでの永続化について調べていたら Core Data がよく登場していたので最初は Core Data を使おうと思っていましたが、プロジェクト作成時の Storage の箇所に SwiftData という文言があって調べてみたら、どうやら WWDC 2023 で公開されたばかりの新機能のようです。せっかくなので使ってみたいと思います。
余談ですが、こういう新しい技術は仕事の場合は情報量が少ない内は極力使いたくないですが、個人プロジェクトだったらどんどん使っていく方針です。
作るアプリの概要
以下のメモの作成、表示、削除機能を実装します。
ソースコード
https://github.com/tkugimot/EasyMemo
実装の概要
以下のファイルを追加します。
ファイル名 | 役割 |
---|---|
Domains/Memo/Memo | SwiftDataで扱うメモのモデル |
Screens/MemoListScreen | メモの表示と削除、新規作成への遷移元を表示する画面 |
Views/AddMemoView | 新規のメモを追加する画面 |
実装
プロジェクトの作成
EasyMemo
という Product Name でアプリを作成します。Storageに SwiftData
を指定します。
Item というモデルとそれを使ったサンプル実装が入った状態で作成されます。不要なので適宜削除してください。
保存するデータのモデルを作成
import Foundation
import SwiftData
@Model
final class Memo {
var content: String
var createdAt: Date
var updatedAt: Date
init(content: String) {
self.content = content
createdAt = Date()
updatedAt = Date()
}
}
メモの新規作成画面を作成
struct AddMemoView: View {
@Environment(\.modelContext) private var context
@Environment(\.presentationMode) var presentation
@State private var content: String = ""
var body: some View {
VStack {
Form {
TextField(
text: $content,
prompt: Text("Required"),
axis: .vertical
) {
Text("Content")
}
.lineLimit(5...10)
}
}
.navigationBarTitle("Add new memo")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {add()}) {
Text("Save")
}
}
}
}
private func add() {
let newMemo = Memo(content: content)
context.insert(newMemo)
// go back to previous page
presentation.wrappedValue.dismiss()
}
}
#Preview {
AddMemoView()
}
メモのリスト画面を作成
import SwiftUI
import SwiftData
struct MemoListScreen: View {
@Environment(\.modelContext) private var context
@Query private var memos: [Memo]
var body: some View {
NavigationView {
List {
ForEach(memos) { memo in
NavigationLink {
VStack(alignment: .leading) {
Text(memo.content)
.font(.title)
.frame(
minWidth: 0,
maxWidth: .infinity,
minHeight: 0,
maxHeight: .infinity,
alignment: .topLeading)
.padding()
}
.padding()
} label: {
Text(memo.content)
.font(.subheadline)
.frame(maxWidth: /*@START_MENU_TOKEN@*/.infinity/*@END_MENU_TOKEN@*/, alignment: .leading)
.lineLimit(1)
}
}
.onDelete(perform: { indexSet in
for index in indexSet {
delete(memo: memos[index])
}
})
}
.navigationTitle("Memo")
.navigationBarTitleDisplayMode(.automatic)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
NavigationLink(destination: AddMemoView()) {
Text("Add")
}
}
}
}
}
private func delete(memo: Memo) {
context.delete(memo)
}
}
#Preview {
MemoListScreen()
.modelContainer(for: Memo.self)
}
ContentViewを修正
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
var body: some View {
MemoListScreen()
}
}
#Preview {
ContentView()
.modelContainer(for: Memo.self, inMemory: true)
}
感想
正直 SwiftData の使い易さにかなりビックリしました。ボイラープレート的なコードも全然なく、無駄のないinterface設計に感銘を受けました。最近のSwiftは全体的にかなり実装し易く整理されている印象で、今すごくアプリ開発に前向きになれています。Appleの皆さんありがとうございます。