Cannot pass function of type '() async -> Void' to parameter expecting
💡Tips
SwiftUIのButtonの中には、非同期処理をそのまま書けないらしい?
Firestoreにデータをaddする処理をそのまま書くとエラーが出る???
他の言語だったらasync/await
を書くけどSwiftUIはどうすれば良いのか?
Task.iniというクロージャーを追加すればいいらしい?
// これで囲む
Task.ini {
// 処理を書く
}
init(priority:operation:)
Runs the given throwing operation asynchronously as part of a new top-level task on behalf of the current actor.
init(優先度:操作:) 現在のアクターに代わって、新しいトップレベル タスクの一部として、指定されたスロー操作を非同期で実行します。
デモアプリを作る
デモアプリの作り方は、FirebaseのiOS SDKを追加して、addするメソッドを追加するだけ。Storyboardよりは簡単ですね。
アプリのエントリーポイントのファイルに、FirebaseCoreの設定をする。
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()
}
}
}
デフォルのアプリのUIを作るコードに、Firestoreにaddする処理を追加する。
import SwiftUI
import FirebaseFirestore
struct ContentView: View {
@State var textValue: String = ""
let db = Firestore.firestore()
var body: some View {
VStack {
TextField("本を追加", text: $textValue)
.textFieldStyle(RoundedBorderTextFieldStyle())
Button(action: {
Task.init {
// Add a new document with a generated ID
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)")
}
}
}) {
Text("本を追加")
.bold()
.padding()
.frame(width: 100, height: 50)
.foregroundColor(Color.white)
.background(Color.orange)
.cornerRadius(25)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Viewとロジックが一緒だとなんだかかっこよくない💦
ロジックをメソッドにまとめて、クラスとして別の場所に配置して、使うときはインスタンス化して呼び出すようにしてみた。
import SwiftUI
import FirebaseFirestore
// ロジックをViewから切り分けてクラスの中にメソッドとして定義
class BookRepository {
let db = Firestore.firestore()
// Firestoreにデータを追加するメソッド
func addBook(textValue: String) async {
// Add a new document with a generated ID
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)")
}
}
}
struct ContentView: View {
@State var textValue: String = ""
// リポジトリクラスをインスタンス化する
let bookRepository = BookRepository()
var body: some View {
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()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
データも保存できているようなので問題なし。
まとめ
今回は、FirestoreをSwiftUIで使う練習をしたかったので、ドキュメントのコードをそのまま使うのをやってみようとしましたが、なかなかうまくいきませんでした💦
非同期処理の書き方のパターンをわかっていないと、使えないな〜悩まされながらコードを書いて試してみました。
参考になった記事:
Discussion