見出し画像

【じっくりSw1ftUI45】実践編15〜第27章  SwiftUI Observable と Environment オブジェクト – チュートリアル

さてと、前回

で、

同時実行性とライフサイクル イベント修飾子

については触れたので、今回は、

データクラスなんかを扱うところをやってく〜〜〜

毎度ゆーてるとおり、オイラの学習なんざ関係ないって人は、

に今回のサンプルコードなんかは載ってるから、そっちでやればいいんじゃね?👀💦
てな感じで、早速

じっくり第27章を読んでく👓

概要だと、

ちょっと前にやった

監視オブジェクトの使い方を今回はやってくぜ

的なことを書いてんね👀
んだば早速、

実際に動かしてこ🕺

import SwiftUI

@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    var body: some View {
        ScrollView{
            VStack{
                
            }
        }
    }
}

#Preview {
    Essentials27ContensView()
}

てな感じでまずは、クラスをセットして〜〜〜
ちょっとオイラの作りで、NavigationViewを挟んじゃうと、面倒くさいことになるので、

import SwiftUI

@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    var body: some View {
        ScrollView{
            VStack{
                E27TimesDataView()
            }
        }
    }
}

struct E27TimesDataView: View {
    //時間クラスデータをセットした変数
    var timesData: E27TimesData = E27TimesData()
    
    var body: some View {
        VStack{
            Text("\(timesData.timesCount)秒")
        }
        .font(.largeTitle)
        .fontWeight(.bold)
        .foregroundStyle(Color.pink)
        Text("経過しました")
        .font(.largeTitle)
        .fontWeight(.bold)
        Button(action: resetCount){
            Text("リセット")
        }

    }
    func resetCount(){
        timesData.resetTimes()
    }
}

#Preview {
    Essentials27ContensView()
}

てな感じでセットして〜〜〜


てな感じ

リセットをタップすると〜〜〜

画像を採取してる間に0秒から7秒経ちました藁🤣

じゃ、次はNavigationViewで遊ぼう

ここはウダウダ書くと非常に長くなるので

import SwiftUI

@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    var body: some View {
            NavigationView(content: {
                NavigationLink(destination: E27TimesDataView()){
                    Text("時間計測画面を開く")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                }
            })
            .padding()
    }
}

struct E27TimesDataView: View {
    //時間クラスデータをセットした変数
    var timesData: E27TimesData = E27TimesData()
    
    var body: some View {
        VStack{
            Text("\(timesData.timesCount)秒")
        }
        .font(.largeTitle)
        .fontWeight(.bold)
        .foregroundStyle(Color.pink)
        Text("経過しました")
        .font(.largeTitle)
        .fontWeight(.bold)
        Button(action: resetCount){
            Text("リセット")
        }
    }
    func resetCount(){
        timesData.resetTimes()
    }
}

#Preview {
    Essentials27ContensView()
}

てなサンプルコードで完成形を用意してみた藁🤣動きとしては、

1番目の画面で。文字をタップすると〜〜〜
てな感じ

つまり、別のビューへ分割したい時には、

NavigationView (Content:)

ちゃんを使って、移動先のビューを

NavigationLink()

で指定すればいいってだけの話で、実は、

普段、記事公開後に追記してるサンプルコードでいつもやってるヤツ😛
👉やったことがない人=難しい
ここまでの記事を動かしながらちゃんと読んでる人=当たり前

ってだけの話

最後にEnvironmentを使って遊んでみよう

ここもすっげ〜シンプルに行くよ🕺

import SwiftUI

@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    //時間クラスデータをセットした変数
    var timesData: E27TimesData = E27TimesData()
    
    var body: some View {
            NavigationView(content: {
                NavigationLink(destination: E27TimesDataView()){
                    Text("時間計測画面を開く")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                }
                //環境プロパティを使えるようにここにセット
                .environment(timesData)
            })
            .padding()
    }
}

struct E27TimesDataView: View {
    //受け取る環境変数をここに定義
    @Environment(E27TimesData.self) var timesData: E27TimesData
    var body: some View {
        VStack{
            Text("\(timesData.timesCount)秒")
        }
        .font(.largeTitle)
        .fontWeight(.bold)
        .foregroundStyle(Color.pink)
        Text("経過しました")
        .font(.largeTitle)
        .fontWeight(.bold)
        Button(action: resetCount){
            Text("リセット")
        }
    }
    func resetCount(){
        timesData.resetTimes()
    }
}

#Preview {
    //ビュー全体で環境変数クラスをやり取りできるようにプレビューにセット
    Essentials27ContensView().environment(E27TimesData())
}

ホイ、これが完成系で〜〜〜動かすと

入り口〜〜〜
てな感じ
動きとしては何も変わってない藁🤣

んだけど、じゃあ何がメリットなの?👀

てゆーと、、、

一方のビューから他方のビューに参照を渡すことなく、両方のビューが同じ TimerData オブジェクトにアクセスできるようになる

👉どの値がアプリの中でやり取りされつつ、安全に制御されているかを把握するのに、コードを見れば分かるから簡単
=シンプルイズビューティフル

って話。

まとめ

ま、普段どーでもいいからすっ飛ばしてるけど、

このサンプルコードがなぜこーなっているか=冒頭のリンク先の書籍自体のサンプルコードと違うか

疑問に思ってるなら、

自分で動かしてやってみ👀

今まで色々書いてきてる内容を理解してれば理由は分かるはず藁😛

最新のXcodeの環境でなんの警告もなく、普通に動くといいけどね👀

今回のコードまとめ

import SwiftUI

@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    //時間クラスデータをセットした変数
    var timesData: E27TimesData = E27TimesData()
    
    var body: some View {
            NavigationView(content: {
                NavigationLink(destination: E27TimesDataView()){
                    Text("時間計測画面を開く")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                }
                //環境プロパティを使えるようにここにセット
                .environment(timesData)
            })
            .padding()
    }
}

struct E27TimesDataView: View {
    //受け取る環境変数をここに定義
    @Environment(E27TimesData.self) var timesData: E27TimesData
    var body: some View {
        VStack{
            Text("\(timesData.timesCount)秒")
        }
        .font(.largeTitle)
        .fontWeight(.bold)
        .foregroundStyle(Color.pink)
        Text("経過しました")
        .font(.largeTitle)
        .fontWeight(.bold)
        Button(action: resetCount){
            Text("リセット")
        }
    }
    func resetCount(){
        timesData.resetTimes()
    }
}

#Preview {
    //ビュー全体で環境変数クラスをやり取りできるようにプレビューにセット
    Essentials27ContensView().environment(E27TimesData())
}

ま、色々自分なりに動かして遊んでみてね〜〜〜

Apple公式

ま、実はNavigationViewは打ち込めば分かるけど、最新の環境だと

が推奨ですでにdeprecatedされてるんだよね😛ま、Swiftは

進化し続ける言語なので〜〜〜

しかし、Swift2.0→3.0の教訓で

破壊的なコードの変更だけはない

みたいだから、ご安心を〜〜〜〜

さてと次回は、

第28章 AppStorage と SceneStorage を使用した SwiftUI データの永続化

をやってく!

記事公開後の前に〜〜ここでいつものよもやま話

オイラ最近、

なんかで、

UiPathの記事

なんかも出し始めておきながら

SwiftUIがメイン=本職な人っていつも言ってる理由

去年、

で出してる記事を最後まで読んでもらうと分かると思うし、UiPathみたいなローコードな開発環境は、2年くらい前に

GoogleAppsScript

で、実務経験も積みながら、

なんかで記事まで結構出してんだけど、

ローコードツールって
コードをそんなに書かなくてもある程度なことはできる
👉裏を返すとある程度以上は結局、コードを書くしかない

ってことを7年くらい前にUIKitで経験していて、結局、

コードベース開発が細かいツールやアプリの開発には最適だから

ってのが結論だから。ま、他の記事でも書いてっけど、

日本の企業は最先端のプログラミング言語の5年は遅い

から。

今になってやっとこ

UiPathにしろ、GASにしろ、TypaScriptにしろローコードツールがやっとこ主流になって、巨額の予算を使って社内で開発者とか市民開発みたいな取り組みをやってる企業もちらほら見るんだけど、結局、多くの予算を使ったところで、最終的には

SwiftUiみたいなAttributeInspectorみたいなローコード機能も網羅したコードベースのフレームワークに対応した言語に戻る

って分かりきってんだよね。正直、今は過渡期で企業側の決裁権を持ってる管理職以上の知識が遅れすぎていて、

ローコードツール=最新ってキラーフレーズに酔いしれてるだけ藁🤣

コードベースでのプログラミングの作法を知らない人に限って、結合を強くしたり、可読性の低いアクティビティ組んだりとかして、改修コストまで考えると、コードベースの方がいいじゃんって結論になる過渡期だから。

なんでAppleが5年前にSwiftUIを出したか

を学ぼうともしてないしね〜〜〜〜。ま、遅れた国のトレンドが今はローコードツールでも、結局、7年もしたらコードベースの開発環境に

原点回帰

するだけだから、iOSでアプリが作りたい、Swiftが好きってだけなら、周りが何と言おうと、

じっくり、ゆっくり自分の好きなことに邁進しとくだけでいい

👉世間のトレンドとか常識なんて、あなたの人生の殆どに一切責任なんか取らないし、少し先の未来も考えずに、今の流行りで無責任にゆーてるだけだから。

オイラなんて、

VBAの資格を11年前に取得してから、その当時から数年間はそんなもん持っていても仕事がないとか、意味がないとか言われながら、それでも好きで福岡市で数少ない求人でエンジニア経験を積ませてもらいながら、

焦らず、腐らず

やってたところに、後追いで

RPAとDXが世界的に普通になって=普及して

今は引く手数多で仕事してるって感じなんで。他の記事でも毎回ゆーてるけど、

自分の好きを毎日楽しく学び続けるだけでいい
👉世間とか常識なんて案外無責任なもん
iOSのアプリ開発が、この遅れた国の数年後のトレンドになってるかなんて誰にもわからないでしょ藁
🤣
育成なんてせずに、トレンドになった途端に、できる人を探す
しか脳がない国なんだから。

今ですら、

ローコードツールなんざ標準機能では大したことなんざ何も出来ない
👉だからこそ、ローコード

ってことすらわからない=トレンド=流行りしか追っていない国なんだから。

記事公開後、

いつもどおり、

でやった操作を〜〜〜

てな
てな
てなで
コード
ポイント
リンク

サンプルコード

◾️Essentials27.swift

import SwiftUI
import WebKit

//タイトル
let essentialsChapter27NavigationTitle = "第27章"
let essentialsChapter27Title = "第27章 SwiftUI Observable と Environment オブジェクト – チュートリアル"
let essentialsChapter27SubTitle = "第1節 SwiftUI Observable と Environment オブジェクト – チュートリアル"

//コード
let codeEssentials27 = """
@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    //時間クラスデータをセットした変数
    var timesData: E27TimesData = E27TimesData()
    
    var body: some View {
            NavigationView(content: {
                NavigationLink(destination: E27TimesDataView()){
                    Text("時間計測画面を開く")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                }
                //環境プロパティを使えるようにここにセット
                .environment(timesData)
            })
            .padding()
    }
}

struct E27TimesDataView: View {
    //受け取る環境変数をここに定義
    @Environment(E27TimesData.self) var timesData: E27TimesData
    var body: some View {
        VStack{
            Text("\\(timesData.timesCount)秒")
        }
        .font(.largeTitle)
        .fontWeight(.bold)
        .foregroundStyle(Color.pink)
        Text("経過しました")
        .font(.largeTitle)
        .fontWeight(.bold)
        Button(action: resetCount){
            Text("リセット")
        }
    }
    func resetCount(){
        timesData.resetTimes()
    }
}

#Preview {
    //ビュー全体で環境変数クラスをやり取りできるようにプレビューにセット
    Essentials27ContensView().environment(E27TimesData())
}
"""

//ポイント
let pointEssentials27 = """
記事公開後の前に〜〜ここでいつものよもやま話
オイラ最近、UiPathの記事なんかも出し始めておきながら

SwiftUIがメイン=本職な人っていつも言ってる理由

去年出してる記事を最後まで読んでもらうと分かると思うし、UiPathみたいなローコードな開発環境は、2年くらい前にGoogleAppsScriptで、実務経験も積みながら、記事まで結構出してんだけど、
ローコードツールってコードをそんなに書かなくてもある程度なことはできる
👉裏を返すとある程度以上は結局、コードを書くしかない
ってことを7年くらい前にUIKitで経験していて、結局、コードベース開発が細かいツールやアプリの開発には最適だからってのが結論だから。
ま、他の記事でも書いてっけど、日本の企業は最先端のプログラミング言語の5年は遅いから。
今になってやっとこUiPathにしろ、GASにしろ、TypaScriptにしろローコードツールがやっとこ主流になって、巨額の予算を使って社内で開発者とか市民開発みたいな取り組みをやってる企業もちらほら見るんだけど、結局、多くの予算を使ったところで、最終的には
SwiftUiみたいなAttributeInspectorみたいなローコード機能も網羅したコードベースのフレームワークに対応した言語に戻る
って分かりきってんだよね。正直、今は過渡期で企業側の決裁権を持ってる管理職以上の知識が遅れすぎていて、ローコードツール=最新ってキラーフレーズに酔いしれてるだけ藁🤣
コードベースでのプログラミングの作法を知らない人に限って、結合を強くしたり、可読性の低いアクティビティ組んだりとかして、改修コストまで考えると、コードベースの方がいいじゃんって結論になる過渡期だから。
なんでAppleが5年前にSwiftUIを出したかを学ぼうともしてないしね〜〜〜〜。ま、遅れた国のトレンドが今はローコードツールでも、結局、7年もしたらコードベースの開発環境に原点回帰するだけだから、
iOSでアプリが作りたい、Swiftが好きって
だけなら、周りが何と言おうと、
じっくり、ゆっくり自分の好きなことに邁進しとくだけでいい
👉世間のトレンドとか常識なんて、あなたの人生の殆どに一切責任なんか取らないし、少し先の未来も考えずに、今の流行りで無責任にゆーてるだけだから。
オイラなんて、VBAの資格を11年前に取得してから、その当時から数年間はそんなもん持っていても仕事がないとか、意味がないとか言われながら、それでも好きで福岡市で数少ない求人でエンジニア経験を積ませてもらいながら、
焦らず、腐らずやってたところに、後追いでRPAとDXが世界的に普通になって
=普及して
今は引く手数多で仕事してるって感じなんで。
他の記事でも毎回ゆーてるけど、自分の好きを毎日楽しく学び続けるだけでいい
👉世間とか常識なんて案外無責任なもん。iOSのアプリ開発が、この遅れた国の数年後のトレンドになってるかなんて誰にもわからないでしょ藁🤣
育成なんてせずに、トレンドになった途端に、できる人を探すしか脳がない国なんだから。
今ですら、ローコードツールなんざ標準機能では大したことなんざ何も出来ない
👉だからこそ、ローコード
ってことすらわからない=トレンド=流行りしか追っていない国なんだから。
"""
//URL
let urlEssentials27 = "https://note.com/m_kakudo/n/nd53adabfe8a5"

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentialsCh27: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentialsCh27
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentialsCh27{
    case Sec1
}
//各項目に表示するリスト項目
let dataiOSApp17DevelopmentEssentialsCh27: [ListiOSApp17DevelopmentEssentialsCh27] = [
    ListiOSApp17DevelopmentEssentialsCh27(id: 1, title: essentialsChapter27SubTitle, view: .Sec1),
]
struct iOSApp17DevelopmentEssentialsCh27: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentialsCh27) { data in
                self.containedViewiOSApp17DevelopmentEssentialsCh27(dataiOSApp17DevelopmentEssentialsCh27: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle(essentialsChapter27NavigationTitle)
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentialsCh27(dataiOSApp17DevelopmentEssentialsCh27: ListiOSApp17DevelopmentEssentialsCh27) -> AnyView {
        switch dataiOSApp17DevelopmentEssentialsCh27.view {
        case .Sec1:
            return AnyView(NavigationLink (destination: Essentials27()) {
                Text(dataiOSApp17DevelopmentEssentialsCh27.title)
            })
        }
    }
}
#Preview {
    iOSApp17DevelopmentEssentialsCh27()
}

struct Essentials27: View {
    var body: some View {
        VStack{
            TabView {
                Essentials27ContensView()
                    .tabItem {
                        Image(systemName: contentsImageTab)
                        Text(contentsTextTab)
                    }
                Essentials27Code()
                    .tabItem {
                        Image(systemName: codeImageTab)
                        Text(codeTextTab)
                    }
                Essentials27Points()
                    .tabItem {
                        Image(systemName: pointImageTab)
                        Text(pointTextTab)
                    }
                Essentials27WEB()
                    .tabItem {
                        Image(systemName: webImageTab)
                        Text(webTextTab)
                    }
            }
        }
    }
}
#Preview {
    Essentials27().environment(E27TimesData())
}

struct Essentials27Code: View {
    var body: some View {
        ScrollView{
            Text(codeEssentials27)
        }
    }
}
#Preview {
    Essentials27Code()
}
struct Essentials27Points: View {
    var body: some View {
        ScrollView{
            Text(pointEssentials27)
        }
    }
}
#Preview {
    Essentials27Points()
}
struct Essentials27WebView: UIViewRepresentable {
    let searchURL: URL
    func makeUIView(context: Context) -> WKWebView {
        let view = WKWebView()
        let request = URLRequest(url: searchURL)
        view.load(request)
        return view
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}
struct Essentials27WEB: View {
    private var url:URL = URL(string: urlEssentials27)!
    var body: some View {Essentials27WebView(searchURL: url)
    }
}
#Preview {
    Essentials27WEB()
}

@Observable class E27TimesData{
    var timesCount = 0
    var timer: Timer?
    
    init() {
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true){_ in
            self.updateTimes()
        }
    }
    func updateTimes(){
        timesCount += 1
    }
    
    func resetTimes(){
        timesCount = 0
    }
}

struct Essentials27ContensView: View {
    //時間クラスデータをセットした変数
    var timesData: E27TimesData = E27TimesData()
    
    var body: some View {
            NavigationView(content: {
                NavigationLink(destination: E27TimesDataView()){
                    Text("時間計測画面を開く")
                        .font(.largeTitle)
                        .fontWeight(.bold)
                }
                //環境プロパティを使えるようにここにセット
                .environment(timesData)
            })
            .padding()
    }
}

struct E27TimesDataView: View {
    //受け取る環境変数をここに定義
    @Environment(E27TimesData.self) var timesData: E27TimesData
    var body: some View {
        VStack{
            Text("\(timesData.timesCount)秒")
        }
        .font(.largeTitle)
        .fontWeight(.bold)
        .foregroundStyle(Color.pink)
        Text("経過しました")
        .font(.largeTitle)
        .fontWeight(.bold)
        Button(action: resetCount){
            Text("リセット")
        }
    }
    func resetCount(){
        timesData.resetTimes()
    }
}

#Preview {
    //ビュー全体で環境変数クラスをやり取りできるようにプレビューにセット
    Essentials27ContensView()
}

◾️EssentialsMenu.swift

//フレームワーク
import SwiftUI
import WebKit

//ビュー管理構造体
struct ListiOSApp17DevelopmentEssentials: Identifiable {
    var id: Int
    var title: String
    var view: ViewEnumiOSApp17DevelopmentEssentials
}
//遷移先の画面を格納する列挙型
enum ViewEnumiOSApp17DevelopmentEssentials {
    case Ch1
    //じっくり13で追加
    case Ch2
    //じっくり14で追加
    case Ch3
    //じっくり15で追加
    case Ch4
    //じっくり16で追加
    case Ch5
    //じっくり17で追加
    case Ch6
    //じっくり18で追加
    case Ch7
    //じっくり19で追加
    case Ch8
    //じっくり20、21で追加
    case Ch9
    //じっくり22、23で追加
    case Ch10
    //じっくり24で追加
    case Ch11
    //じっくり25で追加
    case Ch12
    //じっくり26で追加
    case Ch13
    //じっくり27,28で追加
    case Ch14
    //じっくり29で追加
    case Ch15
    //じっくり31で追加
    case Ch16
    //じっくり32で追加
    case Ch17
    //じっくり33で追加
    case Ch18
    //じっくり34で追加
    case Ch19
    //じっくり35で追加
    case Ch20
    //じっくり36で追加
    case Ch21
    //じっくり37で追加
    case Ch22
    //じっくり40で追加
    case Ch23
    //じっくり41で追加
    case Ch24
    //じっくり43で追加
    case Ch25
    //じっくり44で追加
    case Ch26
    //じっくり45で追加
    case Ch27
}
//各項目に表示する文字列
let dataiOSApp17DevelopmentEssentials: [ListiOSApp17DevelopmentEssentials] = [
    ListiOSApp17DevelopmentEssentials(id: 1, title: essentialsChapter1Title, view: .Ch1),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 2, title: essentialsChapter2Title, view: .Ch2),
    //じっくり13で追加
    ListiOSApp17DevelopmentEssentials(id: 3, title: essentialsChapter3Title, view: .Ch3),
    //じっくり15で追加
    ListiOSApp17DevelopmentEssentials(id: 4, title: essentialsChapter4Title, view: .Ch4),
    //じっくり16で追加
    ListiOSApp17DevelopmentEssentials(id: 5, title: essentialsChapter5Title, view: .Ch5),
    //じっくり17で追加
    ListiOSApp17DevelopmentEssentials(id: 6, title: essentialsChapter6Title, view: .Ch6),
    //じっくり18で追加
    ListiOSApp17DevelopmentEssentials(id: 7, title: essentialsChapter7Title, view: .Ch7),
    //じっくり19で追加
    ListiOSApp17DevelopmentEssentials(id: 8, title: essentialsChapter8Title, view: .Ch8),
    //じっくり20、21で追加
    ListiOSApp17DevelopmentEssentials(id: 9, title: essentialsChapter9Title, view: .Ch9),
    //じっくり22、23で追加
    ListiOSApp17DevelopmentEssentials(id: 10, title: essentialsChapter10Title, view: .Ch10),
    //じっくり24で追加
    ListiOSApp17DevelopmentEssentials(id: 11, title: essentialsChapter11Title, view: .Ch11),
    //じっくり25で追加
    ListiOSApp17DevelopmentEssentials(id: 12, title: essentialsChapter12Title, view: .Ch12),
    //じっくり26で追加
    ListiOSApp17DevelopmentEssentials(id: 13, title: essentialsChapter13Title, view: .Ch13),
    //じっくり27,28で追加
    ListiOSApp17DevelopmentEssentials(id: 14, title: essentialsChapter14Title, view: .Ch14),
    //じっくり29で追加
    ListiOSApp17DevelopmentEssentials(id: 15, title: essentialsChapter15Title, view: .Ch15),
    //じっくり31で追加
    ListiOSApp17DevelopmentEssentials(id: 16, title: essentialsChapter16Title, view: .Ch16),
    //じっくり32で追加
    ListiOSApp17DevelopmentEssentials(id: 17, title: essentialsChapter17Title, view: .Ch17),
    //じっくり33で追加
    ListiOSApp17DevelopmentEssentials(id: 18, title: essentialsChapter18Title, view: .Ch18),
    //じっくり34で追加
    ListiOSApp17DevelopmentEssentials(id: 19, title: essentialsChapter19Title, view: .Ch19),
    //じっくり35で追加
    ListiOSApp17DevelopmentEssentials(id: 20, title: essentialsChapter20Title, view: .Ch20),
    //じっくり36で追加
    ListiOSApp17DevelopmentEssentials(id: 21, title: essentialsChapter21Title, view: .Ch21),
    //じっくり37で追加
    ListiOSApp17DevelopmentEssentials(id: 22, title: essentialsChapter22Title, view: .Ch22),
    //じっくり40で追加
    ListiOSApp17DevelopmentEssentials(id: 23, title: essentialsChapter23Title, view: .Ch23),
    //じっくり41で追加
    ListiOSApp17DevelopmentEssentials(id: 24, title: essentialsChapter24Title, view: .Ch24),
    //じっくり43で追加
    ListiOSApp17DevelopmentEssentials(id: 25, title: essentialsChapter25Title, view: .Ch25),
    //じっくり44で追加
    ListiOSApp17DevelopmentEssentials(id: 26, title: essentialsChapter26Title, view: .Ch26),
    //じっくり47で追加
    ListiOSApp17DevelopmentEssentials(id: 27, title: essentialsChapter27Title, view: .Ch27),
]

struct iOSApp17DevelopmentEssentials: View {
    var body: some View {
        VStack {
            Divider()
            List (dataiOSApp17DevelopmentEssentials) { data in
                self.containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: data)
            }
            .edgesIgnoringSafeArea([.bottom])
        }
        .navigationTitle("iOS開発の章目次")
        .navigationBarTitleDisplayMode(.inline)
    }
    //タップ後に遷移先へ遷移させる関数
    func containedViewiOSApp17DevelopmentEssentials(dataiOSApp17DevelopmentEssentials: ListiOSApp17DevelopmentEssentials) -> AnyView {
        switch dataiOSApp17DevelopmentEssentials.view {
        case .Ch1:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh1()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch2:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh2()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり13で追加
        case .Ch3:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh3()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり15で追加
        case .Ch4:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh4()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり16で追加
        case .Ch5:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh5()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり17で追加
        case .Ch6:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh6()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり18で追加
        case .Ch7:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh7()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり19で追加
        case .Ch8:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh8()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり20、21で追加
        case .Ch9:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh9()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり22、23で追加
        case .Ch10:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh10()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり24で追加
        case .Ch11:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh11()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり25で追加
        case .Ch12:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh12()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり26で追加
        case .Ch13:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh13()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり27,28で追加
        case .Ch14:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh14()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり29で追加
        case .Ch15:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh15()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり31で追加
        case .Ch16:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh16()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり32で追加
        case .Ch17:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh17()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり33で追加
        case .Ch18:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh18()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり34で追加
        case .Ch19:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh19()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり35で追加
        case .Ch20:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh20()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり36で追加
        case .Ch21:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh21()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり37で追加
        case .Ch22:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh22()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり40で追加
        case .Ch23:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh23()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり41で追加
        case .Ch24:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh24()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり43で追加
        case .Ch25:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh25()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり44で追加
        case .Ch26:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh26()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
            //じっくり45で追加
        case .Ch27:
            return AnyView(NavigationLink (destination: iOSApp17DevelopmentEssentialsCh27()) {
                Text(dataiOSApp17DevelopmentEssentials.title)
            })
        }
    }
}

#Preview {
    iOSApp17DevelopmentEssentials()
}

◾️SwiftUICatalogforiOS17App.swift

import SwiftUI
import SwiftData

@main
struct SwiftUICatalogforiOS17App: App {
    //じっくり第26章で追加
    @Environment(\.scenePhase) private var scenePhase
    
    var sharedModelContainer: ModelContainer = {
        let schema = Schema([
            Item.self,
        ])
        let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)

        do {
            return try ModelContainer(for: schema, configurations: [modelConfiguration])
        } catch {
            fatalError("Could not create ModelContainer: \(error)")
        }
    }()

    var body: some Scene {
        WindowGroup {
            ContentView()
            //じっくり第27章で追記
                .environment(E27TimesData())
        }
        .modelContainer(sharedModelContainer)
        //じっくり第26章で追加
        .onChange(of: scenePhase){
            switch scenePhase {
            case .background:
                print("バックグラウンドで実行")
            case .inactive:
                print("インアクティブになった")
            case .active:
                print("アクティブになった")
            default:
                print("識別不能なシーンで実行")
            }
        }
    }
}

以上。

冒頭のリンク先のコードとサンプルコード見れば分かると思うけど

  • 別にFoundationをインポートした別ファイルなんていらない

  • .environmentプロパティを各段階でどこに嵌め込めばいいか

ってのを参考にしてもらえると幸い🕺
ビュー単体では教科書どおりにやれば出来ても、総合アプリで、結合した瞬間に、今の2つがわからなくて時間がかかるみたいな人は結構いるからね👀🧐
個人的には、ここまで説明しないと教材のコンテンツではないかなとは思うけどね

ではまた次回=来週かな藁🤣

この記事が気に入ったらサポートをしてみませんか?