Is there a way of converting an array of type Dictionary.Values? into an one-dimension array of type String?
Code:
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data()?.values // Type 'Dictionary<String, Any>.Values?'
self.array.append(dataDescription) // Tried dataDescription.values, but it doesn't work
print("Document data: \(String(describing: dataDescription))")
} else {
print("Document does not exist")
}
}
You can unwrap data as well and then map the values from Any to String:
docRef.getDocument { document, error in
if let document = document, document.exists,
let data = document.data()?.values {
let values = data.values.compactMap{$0 as? String}
print(values)
} else {
print("Document does not exist")
}
}
}
Related
I am working on my first IOS app using Firestore. I am successfully retrieving all user documents or a specific document with a document name from the "users" collection. But as soon as I want to query the currentUsers documents I have trouble. I need the data for a user profile viewController.
I can query a specific document and print it with this code:
func loadUserData() {
let userDocumentRef = Firestore.firestore().collection("users").document(
"6tIzsXgbOMDIFpdgR18i")
userDocumentRef.getDocument { (document, error) in
if let document = document, document.exists {
let userData = document.data().map(String.init(describing: )) ?? "nil"
print("Document data: \(userData)")
} else {
print("document does not exist")
}
}
}
But as soon as I try to get the currentUsers document with this code, then I get the error that "document does not exist":
func currentUserData() {
let userID : String = (Auth.auth().currentUser?.uid)!
let userRef = Firestore.firestore().collection("users").document(userID)
userRef.getDocument { (document, error) in
if let document = document, document.exists {
let userData = document.data().map(String.init(describing: )) ?? "nil"
print("Document data: \(userData)")
} else {
print("document does not exist")
}
}
}
Here is a screenshot of the users collection in Firestore.
Screenshot of Firestore users collection
I just simply cannot figure out what I am doing wrong, so any help is appreciated.
It seems like there is an issue when mapping the document. Here is how you can map data from Firestore to Swift:
struct UserProfile: Codable, Identifiable {
#DocumentID var id: String?
var userId: String
var firstName: String
// ... other attributes
}
func currentUserData() {
// Note: this is unsafe, please use an auth state change listener instead
// Also, please try to avoid using force unwrap - your app will crash if the attribute is nil
let userID : String = (Auth.auth().currentUser?.uid)!
let userRef = Firestore.firestore().collection("users").document(userID)
userRef.getDocument { (document, error) in
if let document = document, document.exists {
let userData = document.data(as: UserProfile.self)
print("Document data: \(userData)")
} else {
print("document does not exist")
}
}
}
Here is how to set up an auth state change listener: https://firebase.google.com/docs/auth/ios/start#listen_for_authentication_state
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)
}
}
im new to swift and Firebase. And im trying to understand it.
I have a Cloud Firestore DB, where i store some user data like username and email. Now i want the output of the given User assign to my UserData Model for easy use. i really dont know how to achiev this.
These are my two Files:
User.swift
import SwiftUI
import Firebase
import FirebaseFirestore
struct User {
var username : String
var email : String
}
GetData.swift
let docRef = db.collection("users").document(Auth.auth().currentUser?.uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
} else {
print("Document does not exist")
}
}
The Variable dataDescription is givin me a dictonaryArray, with all the needed values. And now i need to assign that Dictonary to my User.swift struct.
best regards!
fYI: Firebase Printscreen
I'd recommend using Codable where you can. I'm using the CodableFirebase Cocoapod to help with Firebase parsing. Below is an example how to use this:
let docRef = db.collection("users").document(Auth.auth().currentUser?.uid)
docRef.getDocument { (document, error) in
guard let value = document.value, value as? NSNull == nil else {
return
}
do {
let newValue = try self.decoder.decode(User.self, from: value)
///New value created here
} catch let error {
print(error)
}
}
You can make this more generic by passing through a class type and having an optional instance returned:
static func item<T: Codable>(_ item: T.Type, docRef: DatabaseReference, completion: #escaping ((Result<T?, Error>)->Void)) {
docRef.getDocument { (document, error) in
guard let value = snapshot.value, value as? NSNull == nil else {
completion(.success(nil))
return
}
do {
let newValue = try self.decoder.decode(T.self, from: value)
completion(.success(newValue))
} catch let error {
completion(.failure(error))
}
}
}
try
GetData.swift
var user = User()
let docRef = db.collection("users").document(Auth.auth().currentUser?.uid)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
user.username = document.data(["username"]) as! String
user.email= document.data(["email"]) as! String
} else {
print("Document does not exist")
}
}
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 have migrated user post, followers and following from from firebase to firestore. Now i have migrated post, followers and following and post, followers count too.
Here the code i have migrated from firebase to firestore.
import Foundation
import FirebaseDatabase
import FirebaseFirestore
class FollowApi {
var REF_FOLLOWERS = Database.database().reference().child("followers")
var REF_FOLLOWING = Database.database().reference().child("following")
let db = Firestore.firestore()
func followAction(withUser id: String) {
let docRef = db.collection("user-posts").document(id)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
self.db.collection("feed").document(API.User.CURRENT_USER!.uid).setData([document.documentID: true])
} else {
print("Document does not exist")
}
}
self.db.collection("followers").document(id).setData([API.User.CURRENT_USER!.uid: true])
self.db.collection("following").document(API.User.CURRENT_USER!.uid).updateData([id: true])
}
func unFollowAction(withUser id: String) {
let docRef = db.collection("user-posts").document(id)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
print("Document data: \(dataDescription)")
self.db.collection("feed").document(API.User.CURRENT_USER!.uid).delete()
} else {
print("Document does not exist")
}
}
self.db.collection("followers").document(id).setData([API.User.CURRENT_USER!.uid: NSNull()])
self.db.collection("following").document(API.User.CURRENT_USER!.uid).setData([id: NSNull()])
}
func isFollowing(userId: String, completed: #escaping (Bool) -> Void) {
let docRef = db.collection("followers").document(userId)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
print("documnetData::::\(String(describing: document.data()))")
if let dataDescription = document.data(), let _ = dataDescription[API.User.CURRENT_USER!.uid] as? Bool {
completed(true)
}
completed(false)
} else {
completed(false)
}
}
}
func fetchCountFollowing(userId: String, completion: #escaping (Int) -> Void) {
// REF_FOLLOWING.child(userId).observe(.value, with: {
// snapshot in
// let count = Int(snapshot.childrenCount)
// completion(count)
// })
db.collection("following").document(userId).getDocument { (querySnapshot, error) in
let count = Int((querySnapshot?.documentID)!)
print("followingCount::::\(String(describing: count))")
completion(count!)
}
}
}//followAPI
I tried to get following counts from firestore.
let count = Int((querySnapshot?.documentID)!)
print("followingCount::::\(String(describing: count))")
completion(count!)
but does not show any any yet all. I do not know what mistake i have done ?
Any help much appreciated pls....
If you're querying for a collection then its snapshot will contain an array of documents. What are you trying to get is a documentID which is same as key in Firebase.
Firestore | Firebase
documentID = snapshot.key
documentData = snapshot.value
Now, Come to the main point and here is what you need to get the count.
let count = querySnapshot?.documents.count
print(count)
EDIT For Comment: how can i migrate REF_FOLLOWING.child(userId).observe(.value, with: { snapshot in let count = Int(snapshot.childrenCount) completion(count) }) to firestore
Based on attached DB structure you're fetching following corresponding to userId which is a Document.
REF_FOLLOWING.document(userId).getDocument { (snapshot, error) in
if let _error = error {
print(_error.localizedDescription)
return
}
guard let _snapshot = snapshot else {return}
/// This is a single document and it will give you "[String:Any]" dictionary object. So simply getting its count is the result you needed.
let dict = _snapshot.data()
print(dict.count)
}