I understand that Firestore loads data asynchronously, but I want to use these data later and in different ViewControllers. Is there any possibility, to save data in array?
func findPlayers (filters: Dictionary<String, Any>) -> [String] {
let reference = dataService.instance.dbF.collection("playersStats")
var query1: Query
var keysArray = [String?] ()
var resultIDs = [String] ()
for key in filters.keys {
if key != "PositionName" {
keysArray.append(key)
}
}
if filters.keys.count == 1 {
if keysArray[0] != nil {
let value = filters[keysArray[0]!] as? Double
query1 = reference.whereField(keysArray[0]!, isGreaterThan: value! )
query1.getDocuments{ (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let id = String(document.documentID)
resultIDs.append(id)
}
}
}
}
}
return resultIDs
}
I really expect to see array full of data I want to get.
Related
I made a code that adds likes and shows their number on the screen.
But there is a problem, when you download the application on 2 devices and press the button at the same time, then only one like is counted. How can I fix this without implementing registration?
There is an idea to make fields that will be created for everyone on the phone when the like is pressed and this number will be added to the total, but I do not know how to implement this.
Here's the current code:
struct LikeCounts {
var likecount: String
}
class LikeTextModel: ObservableObject {
#Published var likecounts: LikeCounts!
private var db = Firestore.firestore()
init() {
updateLike()
}
func updateLike() {
db.collection("likes").document("LikeCounter")
.addSnapshotListener { documentSnapshot, error in
guard let document = documentSnapshot else {
print("Error fetching document: \(error!)")
return
}
guard let data = document.data() else {
print("Document data was empty.")
return
}
if let likecount = data["likecount"] as? String {
DispatchQueue.main.async {
self.likecounts = LikeCounts(likecount: likecount)
}
}
}
}
#ObservedObject private var likeModel = LikeTextModel()
if self.likeModel.likecounts != nil{
Button(action:
{self.like.toggle()
like ? addlike(): dellike()
UserDefaults.standard.setValue(self.like, forKey: "like")
}) {
Text((Text(self.likeModel.likecounts.likecount))}
func addlike() {
let db = Firestore.firestore()
let like = Int.init(self.likeModel.likecounts.likecount)
db.collection("likes").document("LikeCounter").updateData(["likecount": "\(like! + 1)"]) { (err) in
if err != nil {
print(err)
return
}
}
}
func dellike() {
let db = Firestore.firestore()
let like = Int.init(self.likeModel.likecounts.likecount)
db.collection("likes").document("LikeCounter").updateData(["likecount": "\(like! - 1)"]) { (err) in
if err != nil {
print(err)
return
}
}
}
Firestore has the ability to reliably increment a value, like this:
db.collection('likes').doc('LikeCounter')
.updateData([
"likecount": FieldValue.increment(1)
]);
I am trying to transform this function so that instead of Printing the firstname, it would instead return it. My goal here is to have a return value so that I can attach the output to a textLabel. Therefore I would have:
WelcomeLabel.text = getDocument()
I don't know if this is the right approach, I would appreciate some guidance.
private func getDocument() {
//Get sspecific document from current user
let docRef = Firestore.firestore().collection("users").whereField("uid", isEqualTo: Auth.auth().currentUser?.uid ?? "")
// Get data
docRef.getDocuments { (querySnapshot, err) in
if let err = err {
print(err.localizedDescription)
return
} else if querySnapshot!.documents.count != 1 {
print("More than one documents or none")
} else {
let document = querySnapshot!.documents.first
let dataDescription = document?.data()
guard let firstname = dataDescription?["firstname"] else { return }
print(firstname)
}
}
}
i am trying to get the current users firstname and type (Welcome, 'firstname') on a textLabel in a view controller
This code adds all the data in a single array. In HomeViev I use to Foreach and I added to data to list. But I have to split the data in two. status collection has two type "active" and "closed" but I don't know how can I filter
import SwiftUI
import Combine
import Firebase
let dbCollection = Firestore.firestore().collection("Signals")
class FirebaseSession : ObservableObject {
#Published var session: User? { didSet { self.didChange.send(self) }}
#Published var data = [Signal]()
var didChange = PassthroughSubject<FirebaseSession, Never>()
var handle: AuthStateDidChangeListenerHandle?
func listen () {
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
if let user = user {
print("Got user: \(user)")
self.session = User(uid: user.uid, email: user.email)
self.readData()
} else {
self.session = nil
}
}
}
func readData() {
dbCollection.addSnapshotListener { (documentSnapshot, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}else {
print("read data success")
}
documentSnapshot!.documentChanges.forEach { i in
// Read real time created data from server
if i.type == .added {
let id = i.document.documentID
let symbol = i.document.get("symbol") as? String ?? ""
let status = i.document.get("status") as? String ?? ""
self.data.append(Signal(id: id, symbol: symbol, status: status))
}
// Read real time modify data from server
if i.type == .modified {
self.data = self.data.map { (eachData) -> Signal in
var data = eachData
if data.id == i.document.documentID {
data.symbol = i.document.get("symbol") as! String
data.status = i.document.get("status") as? String ?? ""
return data
}else {
return eachData
}
}
}
// When data is removed...
if i.type == .removed {
let id = i.document.documentID
for i in 0..<self.data.count{
if self.data[i].id == id{
self.data.remove(at: i)
return
}
}
}
}
}
}
}
The question states
But I have to split the data in two
I assume that means two arrays; one for active and one for closed.
var activeData = [...
var closedData = [...
There are a couple of ways to do that
1)
Query Firestore for all status fields equal to active and load those documents into the active array and then another query for status fields equal closed and load those in the the closed array
2)
I would suggest a simpler approach
if i.type == .added {
let id = i.document.documentID
let symbol = i.document.get("symbol") as? String ?? ""
let status = i.document.get("status") as? String ?? ""
if status == "active" {
self.activeData.append(Signal(id: id, symbol: symbol, status: status))
} else {
self.closedData.append(Signal(id: id, symbol: symbol, status: status))
}
}
and do the same thing within .modified and .removed; identify the status so the code will know which array to remove it from.
EDIT:
Based on a comment
I don't know how to query this codes.
I am providing code to query for signals that are active. This code will return only active signals and as signals become active, inactive etc, this will modify a signalArray to stay in sync with the data.
let dbCollection = Firestore.firestore().collection("Signals")
let query = dbCollection.whereField("status", isEqualTo: "active").addSnapshotListener( { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error fetching snapshots: \(error!)")
return
}
snapshot.documentChanges.forEach { diff in
if (diff.type == .added) {
let signalToAdd = Signal(withDoc: diff.document)
self.signalArray.append(signalToAdd)
}
if (diff.type == .modified) {
let docId = diff.document.documentID
if let indexOfSignalToModify = self.signalArray.firstIndex(where: { $0.signal_id == docId} ) {
let signalToModify = self.signalArray[indexOfSignalToModify]
signalToModify.updateProperties(withDoc: diff.document)
}
}
if (diff.type == .removed) {
let docId = diff.document.documentID
if let indexOfSignalToRemove = self.signalArray.firstIndex(where: { $0.signal_id == docId} ) {
self.signalArray.remove(at: indexOfSignalToRemove)
}
}
}
})
Note that my Signal Class has an initializer that accepts a QueryDocumentSnapshot to initialize it as well as a .updateProperties function to update its internal properties.
I have an array of SKU numbers that I'm returning from Google Firestore "[428024, 4298212]".
I have written a function assigning the array of SKU's to a variable, but I am lost as to how to return that variable from the function.
let db = Firestore.firestore()
func getItems() -> [Int] {
let userID = Auth.auth().currentUser?.uid ?? "nil"
if (session.session != nil) {
self.data.removeAll()
db.collection("users").document(userID).getDocument { (document, error) in
if let document = document, document.exists {
let itemID = document.get("items") as! Array<Int>
print(itemID as Any)
// Prints "[428024, 4298212]"
return itemID
} else {
print("Document does not exist")
}
}
}
}
I'm getting the error "Unexpected non-void return value in void function, though I can see that the array of SKU's are being returned when it runs the "print(itemID as Any)" line.
Is there any mistake in how I have the function written?
Querying the document through Firestore is written using a completion handler and trying to return any value from within this handler to your original function will deliver this error. Instead, you'll need to adjust your original function getItems() to account for this as such:
let db = Firestore.firestore()
#State private var itemIDs: [Int] = []
func getItems(completion: #escaping (_ itemIDs: [Int]?) -> ()) {
let userID = Auth.auth().currentUser?.uid ?? "nil"
if (session.session != nil) {
self.data.removeAll()
db.collection("users").document(userID).getDocument { (document, error) in
if let document = document, document.exists {
let itemIDs = document.get("items") as! Array<Int>
completion(itemIDs) // call completion handler to return value
} else {
print("Document does not exist")
}
}
}
}
func callingYourFunction() {
self.getItems() { itemIDs in
if let ids = itemIDs {
// itemIDs exists -> do whatever else you originally intended to do with the ids
self.itemIDs = ids
}
}
}
Take a look at here if you want to learn more about closures and completion handlers! Hopefully this helps.
I am new to Swift, with a jolted problem. I need to get a list of all documents in the firestore collection. To do this, I use this function:
#Published var chatList: String!
func getDataFromDatabaseListenChat() {
let db = Firestore.firestore()
db.collection("chatRoom").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
self.chatList = document.documentID
print("\(document.documentID) => \(document.data())")
}
}
}
}
If there are more than one document in the collection, it turns out that the next document overwrites the previous one in the variable and at the output I get one element in the list. Tell me how to add all these documents to the array so that they can be used in the swiftui list
You need
var chatList = [String]()
Then
self.chatList = querySnapshot!.documents.map{$0.documentID}
You should avoid force unwrapping where possible. The previous answer suggests using an array, which is correct. But for safety you should do the following:
var chatList = [String]()
func getDataFromDatabaseListenChat() {
let db = Firestore.firestore()
db.collection("chatRoom").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else if let querySnapshot = querySnapshot {
self.chatList = querySnapshot.documents.map { $0.documentID }
}
}
}