Load data from Firebase once - ios

I am using Firebase as my backend service & fetching data using the SnapshotListener ( which keep listening for any data changes ). I want to remove that listener (only run and fetch data on first load).
private func loadShops(_ category: String) {
db.collection(category).addSnapshotListener { snapshot, error in
guard error == nil else {
print(error!.localizedDescription)
return
}
if let docs = snapshot?.documents {
for doc in docs {
self.shops = []
let shopData = doc.data()
var newShop = Shop()
newShop.shopName = shopData["name"] as? String ?? "Empty Name"
self.shops.append(newShop)
self.shops.shuffle()
}
self.shopsView.tableView.reloadData()
}
}
}

Based on the API example above - I'm assuming you are using the Firestore API and not the older Firebase.
If its firestore, you could just use query fetch instead of using a snapshot listener.
Refer to https://firebase.google.com/docs/firestore/query-data/queries#execute_a_query
private func loadShops(_ category: String) {
db.collection(category).getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else if let querySnapshot = querySnapshot {
for document in querySnapshot.documents {
print("\(document.documentID) => \(document.data())")
// do the conversion to model object.
}
}
}
Using the snapshot listener is only if you want to continue monitoring the cloud for new documents. It is also costly as per the pricing documentation.

Related

How to read the current users profile document in Firestore

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

Access Other Projects Firestore Data in iOS

I am using Firestore to build chat between multiple projects using this Doc
i.e. I have 2 applications / Firebase Projects.
App A and App B
Now I have implemented simple collection in Firestore of App A, it works fine without any issues.
My problem is when I want to access Firestore of App A from App B using the above documentation
I am getting error Error Domain=FIRFirestoreErrorDomain Code=7 "Missing or insufficient permissions
I have attached image of collection
Rules are
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true;
}
}
}
The Code I am using to configure on ChatViewController of App B is as follows
let secondaryOptions = FirebaseOptions(googleAppID: "1:27992087142:ios:2a4732a34787067a",
gcmSenderID: "27992087142")
secondaryOptions.apiKey = "AIzaSyBicqfAZPvMgC7NZkjayUEsrepxuXzZDsk"
secondaryOptions.projectID = "projectid-12345"
secondaryOptions.bundleID = "com.google.firebase.devrel.FiroptionConfiguration"
secondaryOptions.databaseURL = "https://myproject.firebaseio.com"
guard let secondary = FirebaseApp.app(name: "secondary")
else { assert(false, "Could not retrieve secondary app") }
// Retrieve a Real Time Database client configured against a specific app.
let _ = Database.database(app: secondary)
var collectionReference:CollectionReference?
collectionReference =
Firestore.firestore().collection("eclinic").document("1002").collection("chats")
collectionReference?.order(by: "timestamp", descending: false).addSnapshotListener { [self] (snapShot, err) in
if let error = err {
print("Error \(error)")
}else{
guard let snapDoc = snapShot?.documents else {
print("Return ")
return
}
}
}
Exploring this documentation link I was able to get over my problem, the code is as follows
let secondaryOptions = FirebaseOptions(googleAppID: "1:27992087142:ios:2a4732a34787067a",
gcmSenderID: "27992087142")
secondaryOptions.apiKey = "AIzaSyBicqfAZPvMgC7NZkjayUEsrepxuXzZDsk"
secondaryOptions.projectID = "projectid-12345"
secondaryOptions.bundleID = "com.google.firebase.devrel.FiroptionConfiguration"
secondaryOptions.databaseURL = "https://myproject.firebaseio.com"
FirebaseApp.configure(name: "secondary", options: secondaryOptions)
// Retrieve a Real Time Database client configured against a specific app.
guard let secondary = FirebaseApp.app(name: "secondary")
else { assert(false, "Could not retrieve secondary app") }
let firestoreSecondary = Firestore.firestore(app: secondary)
var collectionReference:CollectionReference?
collectionReference =
firestoreSecondary.collection("eclinic").document("1002").collection("chats")
collectionReference?.order(by: "timestamp", descending: false).addSnapshotListener { [self] (snapShot, err) in
if let error = err {
print("Error \(error)")
}else{
guard let snapDoc = snapShot?.documents else {
print("Return ")
return
}
}
}

Firestore not returning query Swift 5 Xcode 11.3

I'm currently trying to develop an ios application using Firestore, and when querying the database, I'm getting no results, as neither the if/else block execute. I'm wondering what is going wrong here...
db.collection("users").whereField("uid", isEqualTo: uid).getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error.localizedDescription)")
} else {
for document in querySnapshot!.documents {
weight = document.data()["weight"]! as? Double
}
}
}
Database file structure
Update: I make a call to the database in an earlier method, and this properly returns the user's first name (when I add the weight, it also returns the correct value). But any subsequent calls fail to return anything. Hopefully that info helps.
I have the same Firestore structure like you, and this works for me:
func test() {
var accessLevel: Double?
let db = Firestore.firestore()
db.collection("users").whereField("uid", isEqualTo: UserApi.shared.CURRENT_USER_UID!).getDocuments() { (querySnapshot, error) in
if let error = error {
print("Error getting documents: \(error.localizedDescription)")
} else {
for document in querySnapshot!.documents {
accessLevel = document.data()["accessLevel"]! as? Double
print(accessLevel!)
}
}
}
}
Current uid:
// Actual logged-in User
var CURRENT_USER_UID: String? {
if let currentUserUid = Auth.auth().currentUser?.uid {
return currentUserUid
}
return nil
}
Hope it helps.

How to configure Firebase for Swift and read data out

I have installed Firebase for my Swift IOS App
In my AppDelegate file I import Firebase like this:
import Firebase
import FirebaseDatabase
and initialise it:
FirebaseApp.configure()
After that I check if database is connected and it prints out first "Not connected", but then "Connected", so I assume it is successful
var ref:DatabaseReference!
var databaseHandle:DatabaseHandle!
override func viewDidLoad() {
let connectedRef = Database.database().reference(withPath: ".info/connected")
connectedRef.observe(.value, with: { snapshot in
if snapshot.value as? Bool ?? false {
print("Connected")
} else {
print("Not connected")
}
})
}
But then I try to access my data and it gives me nothing!
ref = Database.database().reference()
databaseHandle = ref.child("Meal").observe(.childAdded, with: { (snapshot) in
print(snapshot.value as Any)
Here is snapshot from console Firebase
Also i can switch to realtime database, where is no data, but there i can allow reading/writing access (I set both to true)
"rules": {
".read": true,
".write": true
}
What could be the problem, so I can't get my data printed out?....
The problem is that you are trying to access Firestore with Firebase methods.
"But then I try to access my data and it gives me nothing!" It gives you nothing because there is no data in Firebase, all your data is in Firestore.
See this guide for Firestore: https://firebase.google.com/docs/firestore/quickstart
and this one to understand the differences between Firebase and Firestore:
https://firebase.google.com/docs/database/rtdb-vs-firestore
There is a difference in accessing Firebase Realtime Database and firestore data
You can access your cloud firestore data as shown below
In ViewDidLoad
let db = Firestore.firestore()
let ref = db.collection("Meal").document("\(myRiderUID!)")
db.collection("Meal").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
}
}
}

How to get data from a map (object) Cloud Firestore document on Swift 4.2?

I am using Cloud Firestore as a Database for my iOS App.
I have an issue while I want to query my data when the document contains Maps (Object Type). I can not access the fields (say: firstName here) of a map (nameOnId) by having the simple query.
My code is as bellow:
let db = Firestore.firestore()
db.collection("userDetails").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let results = document.data()
let result2 = results.compactMap({$0})
print("listedItems: \(document.documentID) => \(result2[0].value)") }}}
I read somewhere that in order to be able to access the values inside the map object, I need to flatten the map object, but having that does not give me access to them, the only thing that I could get into are a group of values inside the map so it only shows the keys and values for them like:
{
firstName = "John";
middleName = "British";
middleName = "Citizen";
gender = "M";
DOB = "8 December 2000 at 00:00:00 UTC+11";
}
the question is how to get access to a single value like "John" using the query?My data structure on Cloud Firestore
One way of doing it is as follows. Also its good practice to not force unwrap your querySnapshot (if the query does not exist, your app will crash!). Hope this helps!
let db = Firestore.firestore()
db.collection("userDetails").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else if let querySnapshot = querySnapshot {
for document in querySnapshot.documents {
let results = document.data()
if let idData = results["nameOnID"] as? [String: Any] {
let firstName = idData["firstName"] as? String ?? ""
print(firstName)
}
}
}
}

Resources