📘

SwiftUIのonAppearとは?

2024/02/11に公開

Overview

https://developer.apple.com/documentation/swiftui/view/onappear(perform%3A)

onAppear(perform:)
Adds an action to perform before this view appears.

onAppear(実行:) このビューが表示される前に実行するアクションを追加します。

Parameters
action
The action to perform. If action is nil, the call has no effect.

パラメーター アクション 実行するアクション。 action が nil の場合、呼び出しは効果がありません。

Return Value
A view that triggers action before it appears.

表示される前にアクションをトリガーするビュー。

Flutterの経験がある私の感覚では、initStateと同じものだろうなと思ってます。画面が呼ばれたら実行する処理を書いておく。今回は、Firestoreから取得したデータを例に解説します。

summary

使い方の例ですが、今回は、Firestoreからデータを取得したらページが呼ばれたときに全てのデータを表示するロジックを使ってみようと思います。

こんな感じでロジックを持っているクラスを定義します。今回は使うのは、getBooksメソッドです。

import Foundation
import FirebaseFirestore

class BookRepository: ObservableObject {
    let db = Firestore.firestore()
    @Published var books: [String] = []
    
    func addBook(textValue: String) async {
        do {
            let ref = try await db.collection("books").addDocument(data: [
                "title": textValue,
                "createdAt": Timestamp(date: Date())
            ])
            print("Document added with ID: \(ref.documentID)")
        } catch {
            print("Error adding document: \(error)")
        }
    }
    
    func getBooks() async {
        do {
            let snapshot = try await db.collection("books").getDocuments()
            books = snapshot.documents.map { document in
                document.data()["title"] as? String ?? ""
            }
        } catch {
            print("Error getting documents: \(error)")
        }
    }
}

getBooksを使うページでは、Listのクロージャの中で、本のタイトルを取得した数だけ表示します。Listには、class modifierの.onAppear追加して、非同期処理をするので、Task.iniの中にgetBooksを定義しているクラスをインスタンス化してプロパティを渡しています。

import SwiftUI

struct ReadBookView: View {
    @ObservedObject var bookRepository = BookRepository()
    
    var body: some View {
        List(bookRepository.books, id: \.self) { book in
            Text(book)
        }
        .onAppear {
            Task.init {
                await bookRepository.getBooks()
            }
        }
    }
}

struct ReadBookView_Previews: PreviewProvider {
    static var previews: some View {
        ReadBookView()
    }
}

こんな感じで、Viewにデータが表示されます。

これだけだと中途半端なので、追加ページとFirebaseを使う設定をしたファイルのソースコードも記載しておきます。FirebaseのiOS用のSDKを追加したら試してみてください。全部で4ファイルあれば使えます。

追加画面はこんな感じ:

Firestoreのデータはこれ:

アプリのエントリーポイントのページ:

import SwiftUI
import FirebaseCore


class AppDelegate: NSObject, UIApplicationDelegate {
  func application(_ application: UIApplication,
                   didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    FirebaseApp.configure()

    return true
  }
}


@main
struct FirestoreCookBookAppApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

追加ページ:

import SwiftUI
import FirebaseFirestore

struct ContentView: View {
    @State var textValue: String = ""
    let bookRepository = BookRepository()
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("本を追加", text: $textValue)
                    .textFieldStyle(RoundedBorderTextFieldStyle())
                Button(action: {
                    Task.init {
                        await bookRepository.addBook(textValue: textValue)
                    }
                }) {
                    Text("本を追加")
                        .bold()
                        .padding()
                        .frame(width: 100, height: 50)
                        .foregroundColor(Color.white)
                        .background(Color.orange)
                        .cornerRadius(25)
                }
            }
            .padding()
            .navigationTitle("本の追加")
            .toolbar {
                ToolbarItem(placement: .navigationBarTrailing) {
                    NavigationLink(destination: ReadBookView()) {
                        Image(systemName: "book")
                    }
                }
            }
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

thoughts

Firestoreのデータを取得して表示したいが、ページが呼ばれたときに、処理を実行するライフサイクルの仕組みを利用する必要があった。この辺知識がまだ足りていないので、知識のinputとoutputを繰り返して理解をしていきたいと思います。

Discussion