Swift 27 Observable

22章'SwiftUI 状態プロパティ、Observable, State, Environment客体'でObservable客体とEnvironment客体について紹介したって、SwiftUIアプリを開発するときもデータ主導方法で実装する方法について説明しました。

今度章には前で学びだった内容を持ってObservable客体とEnvironment客体を利用した簡単な例題プロジェクトを生成するだろう。

# 二十七・壱 ObservableDemoプロジェクトについて
Observable客体は時間が時間が経つにつれて**反復的に変わるデータ値な動的データをラッピングするに使用される時に特に強い**です。そのタープのデータをシミュレーターするためにFoundationフレームウーアクのTimerオブジェクトを使用者して毎秒カウンターがアプデイトする様に構成してObservableデータオブジェクトを生成するだろ。このカウンターはアプリプロジェクト内の`ビュー {`が見られるようにpublishされるだろう。

まずは、データはObservableオブジェクトで扱われるだろうし他のビューで伝達するだろう。今度の章の後半部にはこのデータをEnvironmentオブジェクトで転換して他のビューを伝達しなくてもほかのビューが接近される様にだろう。


# 二十七・弍　プロジェクト生成する
ObservableDemoと言う名前の新しいMultiplatform Appを生成しょう
まず、ObservableObjectプロトコルを実装するデータクラスを追加する事です。
その後、TimerDataで新しいSwift Fileを作った後次のコードを作成して**TimerDataクラスを実装します。**
```diff_swift
import SwiftUI
import Combine

class TimerData: ObservableObject {
    @Published var timeCount: Int = 0
    var timer : Timer?

    init() {
        // Set up a timer to update the counter every second
        timer = Timer.scheduledTimer(timeInterval: 1.0,
                                     target: self,
                                     selector: #selector(timerDidFire),
                                     userInfo: nil,
                                     repeats: true )
    }
    @objc func timerDidFire(){
        timeCount += 1
    }
    func resetCount() {
        timeCount = 0
    }
}
```
このクラスはObservableObjectプロトコルを実装するもので宣言されったし、Timerインスタンスは毎秒timerDidFire()と言う名前の関数を呼び出す様に構成する`init{}`も含まれました。timerDidFire()関数はtimeCount変数で割り当てた値を増加させる。timeCount変数は@Publishedプロパティラッファーを使って宣言されてプロジェクト内であるビューで観察される事ができます。また、このクラスはカウンターを0でリセットするresetCount()と言うメソッドも持っています。

## 弍・一 ContentView`レイアウトvhz`設計する
アプリの使用者インタフェイスは二つの画面で構成される様です。一番ビューはContentView.swift ファイルによって表示されます。そのファイルを選択して次の様に修正しましょう
```diff_swift
truct ContentView: View {
    @StateObject var timerData : TimerData = TimerData()
    
    var body: some View {
        
        NavigationView{
            VStack {
                Text("Timer count = \(timerData.timeCount)")
                    .font(.largeTitle)
                    .fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
                    .padding()
                
                Button(action: resetCount){
                    Text("Reset Counter")
                }
            }
        }
    }
    func resetCount(){
        timerData.resetCount()
    }
}

#Preview {
    ContentView()
}
```
その様に修正したらlive previewを見るとカウンターが増加します。
次には、Reset Counterを押してカウンターが0からまたカウンティングするがを確認します。次には、同一なObservableオブジェクトを接近する２番目のビューを追加する事です。
## 弍・二　２番目のビューを追加する
新しい.Swift ファイルを生成してSecondViewと言うファイルを生成しましょう。
その後、次の様に作成してみましょう
```diff_swift
struct SecondView: View {
    @StateObject var timerData : TimerData
    
    var body: some View {
        
            VStack {
                Text("Second View")
                    .font(.largeTitle)
                Text("Timer count = \(timerData.timeCount)")
                    .font(.headline)
            }
            .padding()
    }
}


struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
        SecondView(timerData: TimerData())
    }
}
```
ライブプリビュウを利用して`レイアウトvhz`をテストすると、ターマがカウンティングを始めるだろう。

ここでこのビューはSecondView_Previews宣言部で構成された自身だけのTimerDataインスタンスを持ちます。ContentViewとSecondView全て同一なTimerDataインスタンスを使用すると使用者が２番目の画面で移動する時一番目ビューにあるObservedObjectオブジェクトをSecodneViewで伝達するべきです。

## 二十七・参　Navigation 追加する
２番目ビューで移動する様にContentViewでNavigation linkが追加されるべきです。次のリンクを追加します。
```diff_swift
  var body: some View {
        
        NavigationView{
            VStack {
                Text("Timer count = \(timerData.timeCount)")
                    .font(.largeTitle)
                    .fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
                    .padding()
                
                Button(action: resetCount){
                    Text("Reset Counter")
                    
+                    NavigationLink(destination: SecondView(timerData: +timerData)){
+                        Text("Next Screen")
+                    }
+                    .padding()
                }
            }
        }
    }
```
また、Previewを使ってContenViewのカウンターが増加するかを確認します。現在のカウンター値を覚えて、Next Screenリンクを押して２番目のビューが表示させる様にして覚えたカウンター数字から続けてカウンティングがつながっているかを確認します。これは二つのビュー全てが同一なObservableオブジェクトインスタンスを購読していることを確認してあげます。

## 二十七・肆　Environmentオブジェクト使う
今度のチュとリアルでする最後の作業はObservableオブジェクトをEnvironmentオブジェクトで変換することです。こうすると二つのビューが同一なTimerDataオブジェクトに対する参照を伝達しなくても接近できるよになるます。

今度の修正作業はTimeData.swiftクラス宣言部を修正する必要がなくて二つのSwiftUIビューファイルで少し修正ずればできます。ContentView.swiftファイルから始めよう。timerDataがも以上SecondViewで伝達されない様にNavigation Linkのdestinationを修正します。また、environmentObject()修正者を追加してtimerDataインスタンスをビュー階層で環境で入れます。
```diff_swift
struct ContentView: View {
    @StateObject var timerData : TimerData = TimerData()
    
    var body: some View {
        
        NavigationView{
            VStack {
                Text("Timer count = \(timerData.timeCount)")
                    .font(.largeTitle)
                    .fontWeight(/*@START_MENU_TOKEN@*/.bold/*@END_MENU_TOKEN@*/)
                    .padding()
                
                Button(action: resetCount){
                    Text("Reset Counter")
                    
+                    NavigationLink(destination: SecondView()){
                        Text("Next Screen")
                    }
                    .padding()
                }
            }
+            .environmentObject(timerData)
        }
    }
    func resetCount(){
        timerData.resetCount()
    }
}
```
次て、SecondView.siwftファイルを修正します。
```diff_swift
import SwiftUI

struct SecondView: View {
+    @EnvironmentObject var timerData : TimerData
    
    var body: some View {
        
            VStack {
                Text("Second View")
                    .font(.largeTitle)
                Text("Timer count = \(timerData.timeCount)")
                    .font(.headline)
            }
            .padding()
    }
}


struct SecondView_Previews: PreviewProvider {
    static var previews: some View {
+        SecondView().environmentObject(TimerData())
    }
}

```
live previewま実際デバイスとかシミュレーターを利用して二つ画面全部同一なカウンターデータで接近するかどうかをプロジェクトをテストしましょう。

# 要約
今度の章で使用者インタフェイ内のビューと動的データをバインディングするためObservableオブジェクトとEnvironmentオブジェクトをどう使用するかを見せるチュートリアルを真似したし、また、Observalbeオブジェクトを実装してプロパティを投稿して、ObservableオブジェクトとEnvironmentオブジェクトを購読する方法も調べてみた.