This is my code
let db = Firestore.firestore()
db.collection("chats").document(userDefaults.string(forKey: "currentGroup")!).collection("messages").document("variable").addSnapshotListener { (snapshot, error) in
if error != nil{
print("Error fetching document")
}
else{
let documentData = snapshot!.data()
print(documentData!["numOfMessages"])
self.numOfMessages = documentData!["numOfMessages"] as! Int
print(self.numOfMessages)
//Get texts and display them
db.collection("chats").document(self.userDefaults.string(forKey: "currentGroup")!).collection("messages").document("\(self.numOfMessages)").getDocument { (document, err) in
let newMessageData = document!.data()
let newMessage = newMessageData!["message"] as! String
let newAuthor = newMessageData!["author"] as! String
let authorLabel = UILabel()
authorLabel.text = newAuthor
self.stackView.addArrangedSubview(authorLabel)
let label = UILabel()
label.text = newMessage
self.stackView.addArrangedSubview(label)
}
}
}
This line self.numOfMessages = documentData!["numOfMessages"] as! Int has an error of
Could not cast value of type 'NSTaggedPointerString' (0x1ed6ed450) to 'NSNumber' (0x1ed6f98c8).
This is every since I deleted the collection messages and replaced it with one of the exact same name
The value that documentData!["numOfMessages"] returns is Optional(1) even though in firebase the value is 2.
This is how the Firestore looks:
Either you are listening to the wrong document (perhaps because of an incorrect user default) or you are unwrapping the value incorrectly. To debug this, try the following and see what the problem actually is. The following is a more idiomatic way of handling documents.
if let currentGroup = userDefaults.string(forKey: "currentGroup") {
print(currentGroup) // this could be your problem
Firestore.firestore().collection("chats").document(currentGroup).collection("messages").document("variable").addSnapshotListener { (snapshot, error) in
if let snapshot = snapshot {
if let numOfMessages = snapshot.get("numOfMessages") as? Int {
print(numOfMessages)
} else {
print("field error")
}
} else if let error = error {
print(error)
}
}
} else {
print("no current group")
}
Related
I want to multiply the two firebase values and get the result to display in the Label, without success
Can anyone give me some advice
func UserInfo(){
let firestoreDB = Firestore.firestore()
firestoreDB.collection("Usermoney").whereField("email", isEqualTo: Auth.auth().currentUser!.email!).addSnapshotListener { snapshot, error in
if error != nil {
print(error?.localizedDescription ?? "Error while getting data from server!" )
}else{
if snapshot?.isEmpty != true && snapshot != nil {
for document in snapshot!.documents{
if let money = document.get("money") as? String{
if let q8 = document.get("q8") as? String{
self.money.text = money
self.q8.text = "+"+q8+"%"
let tip = money * q8
self.win.text = String(format: "$%.2f", tip)
}
}
}
}
}
}
}
Try This
func UserInfo(){
let firestoreDB = Firestore.firestore()
firestoreDB.collection("Usermoney").whereField("email", isEqualTo: Auth.auth().currentUser!.email!).addSnapshotListener { snapshot, error in
if error != nil {
print(error?.localizedDescription ?? "Error while getting data from server!" )
}else{
if snapshot?.isEmpty != true && snapshot != nil {
for document in snapshot!.documents{
if let money = document.get("money") as? String{
if let q8 = document.get("q8") as? String{
self.money.text = money
self.q8.text = "+"+q8+"%"
if let moneyDouble = Double(money), let q8Double = Double(q8){
let tip = moneyDouble * q8Double
self.win.text = String(format: "$%.2f", tip)
}
}
}
}
}
}
}
}
I am getting a document from firebase in swift. However the line isn't being run and is not getting the data.
This is my code:
let db = Firestore.firestore()
db.collection("chats").document(userDefaults.string(forKey: "currentGroup")!).collection("messages").document("variable").getDocument { (document, error) in
if error != nil{
print("error getting document")
}
else{
let documentData = document!.data()
let startNumOfMessages = documentData!["numOfMessages"] as! Int
var messageArray: Array<String> = []
if startNumOfMessages > 0 {
for message in 1...startNumOfMessages{
print(message)
//THIS LINE ISNT RUNNING
db.collection("chats").document(self.userDefaults.string(forKey: "currentGroup")!).collection("messages").document("\(message)").getDocument { (messageDoc, err) in
if err != nil{
print("Error getting message \(message)")
}
else{
if messageDoc!.exists && messageDoc != nil{
let messData = messageDoc!.data()
print(messData!["message"]!)
messageArray.append(messData!["message"] as! String)
}
else{
print("error in document")
}
}
}
}
//Display them
for num in 0...messageArray.count{
let label = UILabel()
label.text = messageArray[num]
self.stackView.addArrangedSubview(label)
}
}
}
}
The line below the comment is the line that isn't running. And the line that says label.text = messageArray[num] displays an error
Fatal error: Index out of range
Showing it doesn't get the data.
You miss the asynchronous way use DispatchGroup ( numbered from 1 to 4 )
let db = Firestore.firestore()
db.collection("chats").document(userDefaults.string(forKey: "currentGroup")!).collection("messages").document("variable").getDocument { (document, error) in
if error != nil{
print("error getting document")
}
else{
let documentData = document!.data()
let startNumOfMessages = documentData!["numOfMessages"] as! Int
var messageArray: Array<String> = []
if startNumOfMessages > 0 {
let g = DispatchGroup() /// 1
for message in 1...startNumOfMessages{
print(message)
//THIS LINE ISNT RUNNING
g.enter() /// 2
db.collection("chats").document(self.userDefaults.string(forKey: "currentGroup")!).collection("messages").document("\(message)").getDocument { (messageDoc, err) in
if err != nil{
print("Error getting message \(message)")
}
else{
if messageDoc!.exists && messageDoc != nil{
let messData = messageDoc!.data()
print(messData!["message"]!)
messageArray.append(messData!["message"] as! String)
}
else{
print("error in document")
}
}
g.leave() /// 3
}
}
g.notify(queue: .main) { /// 4
//Display them
for num in 0...messageArray.count{
let label = UILabel()
label.text = messageArray[num]
self.stackView.addArrangedSubview(label)
}
}
}
}
}
I have been working on Firestore for retrieving data, when I tried to get data from collection->document id-> field. refer the below screen shot, I need to check companyCode matches with user entered companyCode.text
I tried with below code, need to check whether the user entered companyCodeLabel.text matches document "companyCode" and also get documentId. Can anyone suggest how to solve this?
guard let code = companyCodeLabel.text else { return }
let docRef = db.collection("Company").whereField("companyCode", isEqualTo: code).limit(to: 1)
docRef.getDocuments { (querysnapshot, error) in
if error != nil {
print("Document Error: ", error!)
} else {
if let doc = querysnapshot?.documents, !doc.isEmpty {
print("Document is present.")
}
}
}
Even tried to print the field value in collection but still have crash and same error nil
self.db.collection("Company").getDocuments { (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let docId = document.documentID
let compCode = document.get("companyCode") as! String
let compName = document.get("companyName") as! String
print(docId, compCode, compName)
}
}
}
I tried to call in wrong db, I was trying var db = Firestore!,
The correct solutions is
Firestore.firestore().collection("Company").getDocuments { (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let docId = document.documentID
let compCode = document.get("companyCode") as! String
let compName = document.get("companyName") as! String
print(docId, compCode, compName)
}
}
I was following issue with using Firebase/Firestore SDK:
Precondition failed: NSArray element failed to match the Swift Array Element type
Expected FIRQueryDocumentSnapshot but found FIRQueryDocumentSnapshot: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1001.2.63.12/swift/stdlib/public/core/ArrayBuffer.swift, line 346
2019-05-18 19:46:00.020040+0200 App[25051:288337] Precondition failed: NSArray element failed to match the Swift Array Element type
Expected FIRQueryDocumentSnapshot but found FIRQueryDocumentSnapshot: file /BuildRoot/Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-1001.2.63.12/swift/stdlib/public/core/ArrayBuffer.swift, line 346
let listener = self.client
.collection("countries/czechia/cities/\(id.rawValue)/venues")
.addSnapshotListener({ (snapshot, error) in
guard let docs = snapshot?.documents else {
observer.onError(error!)
return
}
let arr: [Venue] = docs.compactMap { doc in // The code crashes on this line
do {
let decoded = try self.decoder.decode(Venue.self, from: doc.data())
return decoded
} catch {
print(error)
return nil
}
}
}
This error it's very common when you work with umbrella framework technique.
I was getting "Precondition failed: NSArray element failed to match the Swift Array Element type
Expected FIRQueryDocumentSnapshot but found FIRQueryDocumentSnapshot bla bla bla bla..." crash error in spite of having just one firebase instance reference to static framework working.
So I had to use the powerful NSObject features in particular Key-Value Coding protocol.
Check this....
let db = Firestore.firestore(app: firebase_instance)
defaultsHelper.write(value: true, key: .isReceivingProspects)
prospectosListener = db.collection("collection_name")
.document("document_name")
.collection("collection_name")
.whereField("condition_parameter", arrayContains: "condition_value")
.addSnapshotListener { querySnapshot, error in
weak var _self = self
guard let snapshot = querySnapshot else {
print("Error fetching document: \(error!)")
return
}
// snapshot.documents or snapshot.documentChanges in a loop produces crash
guard let documents = (snapshot as NSObject).value(forKey: "documentChanges") as? NSArray else { return }
for document in documents {
guard let object = document as? NSObject else { debugPrint("object was nil"); return }
guard let type = object.value(forKey: "type") as? Int else { debugPrint("type was nil"); return }
guard let docs = object.value(forKey: "document") as? NSObject else { debugPrint("document was nil"); return }
guard let data = docs.value(forKey: "data") as? [String: Any] else { debugPrint("data was nil"); return }
guard let fbModel = _self?.documentConverter.convertToNotificationModel(documentData: data) else {
debugPrint("fbModel was nil")
return
}
switch type {
case 0: // Added
_self?.onAddedOrModifiedNotificationEvent(fbModel: fbModel)
case 1: // Modified
_self?.onAddedOrModifiedNotificationEvent(fbModel: fbModel)
case 2: // Removed
_self?.onDeleteNotificationEvent(fbModel: fbModel)
default:
debugPrint("Another option")
}
}
I'm running into a problem when I try to make a request to YQL for stock data, when the symbol (newCompanyStockSymbol) to look up is user-entered. I fetch the stocks in this function:
func handleSave() {
// Fetch stock price from symbol provided by user for new company
guard let newCompanyStockSymbol = stockTextField.text else {
print("error getting text from field")
return
}
var newCompanyStockPrice = ""
let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22\(newCompanyStockSymbol)%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
let json = JSON(data: data!)
if let quotes = json["query"]["results"]["quote"].array {
for quote in quotes {
let ask = quote["Ask"].stringValue
newCompanyStockPrice = ask
}
}
print("new company json: \(json)")
}
guard let newCompanyName = self.nameTextField.text else {
print("error getting text from field")
return
}
guard let newCompanyLogo = self.logoTextField.text else {
print("error getting text from field")
return
}
print("2: The new commpany stock price is: \(newCompanyStockPrice)")
// Call save function in view controller to save new company to core data
self.viewController?.save(name: newCompanyName, logo: newCompanyLogo, stockPrice: newCompanyStockPrice)
self.viewController?.tableView.reloadData()
}
task.resume()
// Present reloaded view controller with new company added
let cc = UINavigationController()
let companyController = CompanyController()
viewController = companyController
cc.viewControllers = [companyController]
present(cc, animated: true, completion: nil)
}
And I use string interpolation to insert \(newCompanyStockSymbol) into the request URL at the appropriate place. However I get a crash and error on that line because it's returning nil, I expect because it's using the URL with \(newCompanyStockSymbol) in there verbatim, instead of actually inserting the value.
Is there another way to do this?
EDIT
And the save function in view controller that's called from handleSave() above if it's helpful:
func save(name: String, logo: String, stockPrice: String) {
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let entity =
NSEntityDescription.entity(forEntityName: "Company",
in: managedContext)!
let company = NSManagedObject(entity: entity,
insertInto: managedContext)
company.setValue(stockPrice, forKey: "stockPrice")
company.setValue(name, forKey: "name")
company.setValue(logo, forKey: "logo")
do {
try managedContext.save()
companies.append(company)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
tableView.reloadData()
}
Supposing you entered AAPL in your stockTextField, using simply:
let newCompanyStockSymbol = stockTextField.text
results in newCompanyStockSymbol being:
Optional("AAPL")
which is not what you want in your URL string. The critical section ends up like this:
(%22Optional("AAPL")%22)
Instead, use guard to get the value from the text field:
guard let newCompanyStockSymbol = stockTextField.text else {
// handle the error how you see fit
print("error getting text from field")
return
}
Now your URL should be parsed correctly.
--- Additional info ---
I'm not entirely sure of the rules on 'continued conversation' around here, but hopefully editing this will be acceptable... anyway...
Make sure you are following this flow:
func handleSave() {
let newCompanyName = nameTextField.text
let newCompanyStockSymbol = stockTextField.text
let newCompanyLogo = logoTextField.text
var newCompanyStockPrice = ""
// Fetch stock price from symbol provided by user for new company
let url = URL(string: "https://query.yahooapis.com/v1/public/yql?q=select%20symbol%2C%20Ask%2C%20YearHigh%2C%20YearLow%20from%20yahoo.finance.quotes%20where%20symbol%20in%20(%22\(newCompanyStockSymbol)%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
} else if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
let json = JSON(data: data!)
if let quotes = json["query"]["results"]["quote"].array {
for quote in quotes {
let ask = quote["Ask"].stringValue
newCompanyStockPrice = ask
// task completed, we've parsed the return data,
// so NOW we can finish the save process and
// update the UI
viewController?.save(name: newCompanyName!, logo: newCompanyLogo!, stockPrice: newCompanyStockPrice)
}
}
}
}
task.resume()
}
I'm not testing this, so it might need a tweak, and your .save() function may need to be forced onto the main thread (since it's doing UI updates). But maybe that's a little more clear.