
【じっくりSw1ftUI72】実践(DB)編41(ラスト)〜第53章 SwiftDataチュートリアル〜長かった連載も今日でひと区切り笑🤣
さてと、前回
で、
SwiftDataのガイド
についてはやったけど、例の如く、
今回やるコードを、抜粋して、部分部分で解説してただけ
あんなの、あれだけ読んでわかったってゆーてたら逆に怖い😱
(ま、どのプログラミング言語を使うどこの現場でも、自称さんほど、
それでもわかるのがプロ=分からないが素直に言えない
👉だから一生、自称プロで終わる人で多いのだが。)
今回は、実際のコードを見ながら、作り込んでく〜〜〜
SwiftUIで使えるデータベース関連の記事は、
◾️CoreData
◾️CloudKit
◾️SwiftData
今回の記事
って感じで、この本
のプロットに従って、順番にやってきてはいるんだけど、おそらく、
(DB周りの)各フレームワークの特徴を、
簡単に比較した記事なんかが欲しい
って人もいるだろうから、
で書いたみたいなちょいコラで、次章以降の番外編に行く前に、記事にしようかなと😛ま、今回のSwiftDataまでで、
SwiftUIを使ったiOSの大抵のスマホアプリは、
自分で作れるような知識は網羅できたつもり
だから、これまでの記事で何回か書いてきてると思うけど、基本のコードと動きについては、
で書いてきた今までの記事を見返せばわかるはずだから、そんなもん覚えようとするより、
自分が作りたいアプリを考える方が重要かなあ🧐
それさえ決まれば、後は基本のコードを組み合わせて、自分なりのアプリを作れば良いだけなんで〜〜〜〜
毎度、オイラの学び直しなんざ関係ないって人は、
に全部コードは載ってそうだから、そっちを参考にしたら良いんじゃね
(いつまでそこのサイトが有効かは知らないけど藁😛)
さてと、んだば早速本題へ〜〜〜
じっくり第53章を読んでく👓
ま、ここではチャチャっと、これから前回やったSwiftDataのガイドの実践例をデモンストレーションでやってくぜ🕺って感じで書いてんね🤣こんな解説を掘り下げても仕方ないので〜〜〜
さっそく操作
まずは、今回用のプロジェクトを今回も作成
前回と同じ要領で作成して、余計なコードやファイルを今回は一切削除して、


くらいで良いかなと👀
モデルクラスを作成
import SwiftUI
import SwiftData
import Foundation
@Model
class Menu {
var name: String
var amount: String
init(name: String, amount: String) {
self.name = name
self.amount = amount
}
}

ログ用のモデルクラスを追加
@Model
class FruitsLog {
var dateEntry: Date
init(dateEntry: Date) {
self.dateEntry = dateEntry
}
}

モデルコンテナをAppファイルに追加
import SwiftUI
import SwiftData
@main
struct E53SwiftDataTutirialsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for:Menu.self)
}
}

項目リストビューを作って、アクセスとクエリを追加
struct MenuListView: View {
@Environment(\.modelContext) var modelContext
@Query var fruitsMenu: [Menu]
var body: some View {
List{
}
}
}

MenuListViewの中身を構築
struct MenuListView: View {
@Environment(\.modelContext) var modelContext
@Query var fruitsMenu: [Menu]
var body: some View {
List{
ForEach(fruitsMenu){ menu in
NavigationLink(value: menu){
if(menu.amount.isEmpty){
Text("新しい項目を編集")
.foregroundStyle(Color.gray)
} else {
Text("\(menu.amount),\(menu.name)")
}
}
}
.onDelete(perform: deleteMenu)
}
}
func deleteMenu(_ indexSet: IndexSet){
for idx in indexSet {
let menu = fruitsMenu[idx]
modelContext.delete(menu)
}
}
}
#Preview {
MenuListView()
}

モデルクラスとのリレーション(相関関係)を確立
@Model
class Menu {
var name: String
var amount: String
@Relationship(deleteRule: .cascade) var menuLogs = [FruitsLog]()
init(name: String, amount: String) {
self.name = name
self.amount = amount
}
}
項目詳細ビューを追加して、バインド
struct MenuDetailView: View {
@Bindable var menu: Menu
var body: some View {
Form{
Section("メニュー"){
TextField("名前", text: $menu.name)
TextField("個数", text: $menu.amount)
}
Section("追加履歴"){
Button("履歴追加",action: addMenu)
ForEach(menu.menuLogs){ log in
Text(log.dateEntry.formatted(date: .abbreviated, time: .shortened))
}
}
}
.navigationTitle("項目詳細")
.navigationBarTitleDisplayMode(.inline)
}
func addMenu() { menu.menuLogs.append(FruitsLog(dateEntry: Date.now))
}
}
#Preview {
MenuDetailView(menu: Menu(name: "", amount: ""))
}
てな感じで、

で次にやっとこコンテンツビュー
相変わらず、本だけだと、説明が薄い+プレビューで実行もできないコードなので〜〜〜
struct ContentView: View {
@Environment(\.modelContext) var modelContext
var body: some View {
NavigationStack{
MenuListView()
.navigationTitle("メニュー")
.navigationDestination(for: Menu.self, destination: MenuDetailView.init).toolbar{
Button("新メニュー",systemImage: "plus",action: addMenu)
}
}
}
func addMenu(){
let menu = Menu(name: "", amount: "")
modelContext.insert(menu)
}
}
#Preview {
ContentView().modelContainer(for: Menu.self, inMemory: true)
}

ここまででプレビューで実行してみると




まではできたことを確認
後は、文字検索機能を追加
まずはContentsViewを
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@State private var searchName = ""
var body: some View {
NavigationStack{
MenuListView(searchName:searchName)
.navigationTitle("メニュー")
.searchable(text: $searchName)
.navigationDestination(for: Menu.self, destination: MenuDetailView.init).toolbar{
Button("新メニュー",systemImage: "plus",action: addMenu)
}
}
}
func addMenu(){
let menu = Menu(name: "", amount: "")
modelContext.insert(menu)
}
}
#Preview {
ContentView().modelContainer(for: Menu.self, inMemory: true)
}
てな感じに書き換えて、MenuListViewに対応するイニシャライザとクエリを追加〜〜〜
struct MenuListView: View {
@Environment(\.modelContext) var modelContext
@Query var fruitsMenu: [Menu]
var body: some View {
List{
ForEach(fruitsMenu){ menu in
NavigationLink(value: menu){
if(menu.amount.isEmpty){
Text("新しい項目を編集")
.foregroundStyle(Color.gray)
} else {
Text("\(menu.amount),\(menu.name)")
}
}
}
.onDelete(perform: deleteMenu)
}
}
init(searchName:String){
_fruitsMenu = Query(filter: #Predicate{
if searchName.isEmpty{
return true
} else {
return $0.name.localizedStandardContains(searchName)
}
})
}
func deleteMenu(_ indexSet: IndexSet){
for idx in indexSet {
let menu = fruitsMenu[idx]
modelContext.delete(menu)
}
}
}
#Preview {
MenuListView(searchName: "")
}
プレビューで確認(完成形)








シミュレーターで実行


一回、実行し直して〜〜〜

ほい、データベースとしても画面との連携も、永続性も問題なし。
今回のコードまとめ
◾️E53SwiftDataTutirialsApp.swift
import SwiftUI
import SwiftData
@main
struct E53SwiftDataTutirialsApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for:Menu.self)
}
}
◾️ContentView.swift
import SwiftUI
import SwiftData
import Foundation
@Model
class Menu {
var name: String
var amount: String
@Relationship(deleteRule: .cascade) var menuLogs = [FruitsLog]()
init(name: String, amount: String) {
self.name = name
self.amount = amount
}
}
@Model
class FruitsLog {
var dateEntry: Date
init(dateEntry: Date) {
self.dateEntry = dateEntry
}
}
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@State private var searchName = ""
var body: some View {
NavigationStack{
MenuListView(searchName:searchName)
.navigationTitle("メニュー")
.searchable(text: $searchName)
.navigationDestination(for: Menu.self, destination: MenuDetailView.init).toolbar{
Button("新メニュー",systemImage: "plus",action: addMenu)
}
}
}
func addMenu(){
let menu = Menu(name: "", amount: "")
modelContext.insert(menu)
}
}
#Preview {
ContentView().modelContainer(for: Menu.self, inMemory: true)
}
struct MenuListView: View {
@Environment(\.modelContext) var modelContext
@Query var fruitsMenu: [Menu]
var body: some View {
List{
ForEach(fruitsMenu){ menu in
NavigationLink(value: menu){
if(menu.amount.isEmpty){
Text("新しい項目を編集")
.foregroundStyle(Color.gray)
} else {
Text("\(menu.amount),\(menu.name)")
}
}
}
.onDelete(perform: deleteMenu)
}
}
init(searchName:String){
_fruitsMenu = Query(filter: #Predicate{
if searchName.isEmpty{
return true
} else {
return $0.name.localizedStandardContains(searchName)
}
})
}
func deleteMenu(_ indexSet: IndexSet){
for idx in indexSet {
let menu = fruitsMenu[idx]
modelContext.delete(menu)
}
}
}
#Preview {
MenuListView(searchName: "")
}
struct MenuDetailView: View {
@Bindable var menu: Menu
var body: some View {
Form{
Section("メニュー"){
TextField("名前", text: $menu.name)
TextField("個数", text: $menu.amount)
}
Section("追加履歴"){
Button("履歴追加",action: addMenu)
ForEach(menu.menuLogs){ log in
Text(log.dateEntry.formatted(date: .abbreviated, time: .shortened))
}
}
}
.navigationTitle("項目詳細")
.navigationBarTitleDisplayMode(.inline)
}
func addMenu() {
menu.menuLogs.append(FruitsLog(dateEntry: Date.now))
}
}
#Preview {
MenuDetailView(menu: Menu(name: "", amount: ""))
}
💃めっちゃシンプル🕺
Apple公式
さてと、次回は
冒頭に書いたとおり、今回で1年に及んだ、
SwiftUIの連載記事【じっくりSw1ftUI72】シリーズ
も実践編まで無事完了🕺
以降は、
てな感じで、
Widgetキットが主な話=スマホアプリの付属機能=番外編
なので、、、
で書いてたとおり、今回で
💃本編は終了🕺
後は、気が向いた時にサラッと書くかな🧐
くらいなもん🤣
ではでは、ここまでのご愛読ありがとうございました。
基本的なアプリのビューとデータベースの連携まで含めて、SwiftUIでのアプリ開発の基本は全て網羅したつもりなので、後は、自分でこれまでの記事を参考に、自分なりのアプリを作ってみてね〜〜〜
次回は、冒頭で書いたとおり、ちょいコラで、
ここまでの振り返りとSwiftUIのDB周りの機能比較なぞ
を書きまする(余裕があれば、明日やも😛)
記事公開後、
今回も特になし。