🪶

SwiftUI Observation

2024/08/05に公開

Observation

Make responsive apps that update the presentation when underlying data changes.

観察
基礎となるデータが変更されたときにプレゼンテーションが更新されるレスポンシブ・アプリを作成する。

日本語の字幕付きで公式を見ていただいた方が良いと思う。
https://developer.apple.com/jp/videos/play/wwdc2023/10149/

Observationは、プロパティの変更を追跡するためのSwiftの新機能です。と解説されている。マクロのマジックで動作するらしい?

モデルを定義するときに、型を自動で生成してくれるとか?

Honoと呼ばるFWで作成したREST APIからデータをHTTP GETする例で使い方を解説します。

structでモデルを作成する。

import Foundation

struct Product: Identifiable, Codable {
    let id: String
    let name: String
    let description: String
}

Combineで作ったコードと比較してもそこまで変わらない気がするが、APIからデータを取得するコードを簡潔に、書けている気がする。

import SwiftUI
import Observation

@Observable
class ProductManager {
    var products: [Product] = []
    var isLoading = false
    var errorMessage: String?
    
    func fetchProducts() {
        isLoading = true
        errorMessage = nil
        
        guard let url = URL(string: "https://hono-shop-app.gitonly543.workers.dev/api/shop") else {
            errorMessage = "Invalid URL"
            isLoading = false
            return
        }
        
        URLSession.shared.dataTask(with: url) { [weak self] data, response, error in
            DispatchQueue.main.async {
                self?.isLoading = false
                
                if let error = error {
                    self?.errorMessage = error.localizedDescription
                    return
                }
                
                guard let data = data else {
                    self?.errorMessage = "No data received"
                    return
                }
                
                do {
                    let decodedProducts = try JSONDecoder().decode([Product].self, from: data)
                    self?.products = decodedProducts
                } catch {
                    self?.errorMessage = "Failed to decode data: \(error.localizedDescription)"
                }
            }
        }.resume()
    }
}

Viewに表示するときは、ObservableObjectを使わなかった。いつもなら、ViewModelのようなクラスを作って、やってるのだが、裏側でそんな機能を作ってくれているのか???

import SwiftUI

struct ContentView: View {
    @State private var productManager = ProductManager()
    
    var body: some View {
        NavigationStack {
            Group {
                if productManager.isLoading {
                    ProgressView()
                } else if let errorMessage = productManager.errorMessage {
                    Text(errorMessage)
                        .foregroundColor(.red)
                } else {
                    List(productManager.products) { product in
                        VStack(alignment: .leading) {
                            Text(product.name)
                                .font(.headline)
                            Text(product.description)
                                .font(.subheadline)
                                .foregroundColor(.secondary)
                        }
                    }
                }
            }
            .navigationTitle("Products")
        }
        .onAppear {
            productManager.fetchProducts()
        }
    }
}

#Preview {
    ContentView()
}

プレビューで確認するとこんな感じで、APIからデータを取得して表示できます。

感想

複雑な処理を書かずともコードを書けているように感じた。Combineだともっと多くの処理を書いていた気がする。View状態が変更されるロジックを書くなら、Observationを使うと良いかもしれない。

私の場合は、いつもObservableObjectを使ったクラスを使っていますね。

Discussion