Hi guys,
retrieve Data from a single Document works fine, but now I want to have a Textfield where the User can search for a single Document inside my Firestore Database.
FireStoreManager.swift:
import Firebase
import SwiftUI
class FirestoreManager: ObservableObject {
@Published var name: String = “”
@Published var elevation: String = “”
@Published var runway: String = “”
@Published var searchairport: String = “”
init() {
fetchRestaurant()
}
func fetchRestaurant() {
let db = Firestore.firestore()
let docRef = db.collection("Airports").document(searchairport)
// variable for searching the airport ???
docRef.getDocument { (document, error) in
guard error == nil else {
print("error", error ?? "")
return
}
if let document = document, document.exists {
let data = document.data()
if let data = data {
print("data", data)
self.name = data["name"] as? String ?? ""
self.elevation = data["elevation"] as? String ?? ""
self.runway = data["runway"] as? String ?? ""
}
}
}
}
}
ContentView looks like this:
struct ContentView: View {
@EnvironmentObject var firestoreManager: FirestoreManager
var body: some View {
TextField("Enter Airportname", text: $searchairport )
Text("ICAO: \(firestoreManager.name)")
Text("Elevation: \(firestoreManager.elevation)")
Text("Runway: \(firestoreManager.runway)")
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(FirestoreManager())
}
}
My Firestore looks like this
In my opinion, a better way to do this is to allow Firebase to create a record using the Auto documentID process rather than going to the trouble of specifying a specific document ID since you run the risk of creating a duplicate documentID accidentally. They are supposed to be unique.
See this firebase screenshot example:
Ignore the fact that in the above example airports is not capitalised
In your ObservableObject your createAirport function could be like this:
func createAirport(name: String, elevation: String, runway: String) {
let db = Firestore.firestore()
db.collection("Airports").addDocument(data: [
"name": name,
"elevation": elevation,
"runway": runway
])
}
Then your fetchAirport function (I named the function fetchAirport because it’s not a restaurant) would be something like this:
func fetchAirport() {
let db = Firestore.firestore()
let query = db.collection("Airports")
.whereField("name", isEqualTo: searchAirport)
query.getDocuments { snapshot, error in
guard error == nil, snapshot != nil else {
print("Error: \(error?.localizedDescription ?? "")")
return
}
// Assuming that the Airport ID is always going to be unique
// there will only be 1 record
for doc in snapshot!.documents {
self.name = doc["name"] as? String ?? ""
self.elevation = doc["elevation"] as? String ?? ""
self.runway = doc["runway"] as? String ?? ""
}
}
}
This is a query where the documents are searched to find a match where the name
field is equal to the search criteria.
I hope that helps.
2 Likes
Hi Chris,
thank you for the better approach with the query and the equal to.
Do I need to connect my variable searchAirport in the Textfield with a button to start the search query in the ContentView() ?
struct ContentView: View {
@EnvironmentObject var firestoreManager: FirestoreManager
var body: some View {
TextField("Enter Airportname", text: $firestoreManager.searchAirport)
//Searchfield with the variable which is equal to name
Text("ICAO: \(firestoreManager.name)")
Text("Elevation: \(firestoreManager.elevation)")
Text("Runway: \(firestoreManager.runway)")
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
.environmentObject(FirestoreManager())
}
}
I just got it to work with a button
Thank you so much for your help Chris. I am happy that my project comes finally alive
Solution
struct ContentView: View {
@EnvironmentObject var firestoreManager: FirestoreManager
@State private var searchAirport: String = “”
var body: some View {
TextField("Enter Airportname", text: $firestoreManager.searchAirport)
Button {
firestoreManager.fetchAirport()
} label: {
Text("Save Airport")
}
The other way you can do this is use the .onSubmit modifier on the TextField which responds to when you press the return key.
ie:
TextField("Enter Airportname", text: $firestoreManager.searchAirport)
.textInputAutocapitalization(.characters)
.textFieldStyle(.roundedBorder)
.onSubmit(of: .text, {
// This responds to pressing return on the TextField
firestoreManager.fetchAirport()
})
The other two modifiers:
.textInputAutocapitalization(.characters)
converts all alphabetic entries to uppercase (very handy since your airport identifiers are uppercase) and
.textFieldStyle(.roundedBorder)
just makes the TextField look a bit nicer on the screen as per this example code I set up to test the code I provided you previously.
2 Likes
Thank you for the tips and hints, I implemented them for better comfort.