Can't retrieve data from Firebase Database - ios

I my code I can't retrieve data from Firebase.
When I am adding values on Firebase I do see that items added on Firebase database on my console.
Also I had created listener .childAdded and I see that items added on Firebase Database.
But when I call .value it retrieves nil result.
I don't know what's happening there.
Here is my code.
class FirebaseManager {
var ref: DatabaseReference?
var database: DatabaseHandle?
static let shared: FirebaseManager = {
Database.database().isPersistenceEnabled = true
return FirebaseManager()
}()
private func initFireabase() {
ref = Database.database().reference(withPath: AMUtils.getUDID())
}
func addToDB(composition: Composition) {
initFireabase()
ref?.child(String(composition.id)).child("id").setValue(composition.id)
ref?.child(String(composition.id)).child("title").setValue(composition.title)
}
func removeFromDb(composition: Composition) {
ref?.child(String(composition.id)).removeValue()
}
func getCompositonFromDB(onResult: #escaping ([Composition]) -> Void){
initFireabase()
var compositions: [Composition] = []
database = ref?.observe(.value, with: { (snapshot) in
compositions.removeAll()
let value = snapshot.value as? JSON
compositions.append(AudioListParser.parseObject(json: value!))
self.ref?.removeObserver(withHandle: self.database!)
onResult(compositions)
})
}
}
getCompositonFromDB() I am calling when I am starting the view controller and this is always nil even I have values on database
Could anyone tell me what I did wrong here?

have you changed the rules for database??
like if you are authenticating you should set not null for both write and read and if you are not authenticating then keep it null for both, because doing exact opposite of above could cause some errors!!

Related

How to Implement User-Specific Favourites using Firestore?

My project contains an array of MealPlans. Right now, a user can star a MealPlan and that will update the isStarred Bool value in the MealPlan document. However, this just updates the database that every user currently accesses.
How can I code it so that a user has their own personal set of isStarred MealPlans?
I'm currently using Firebase Authentication and Firestore. This is my MealPlan struct:
struct MealPlan {
var docID:String?
var title:String?
var recipeSource:String?
var estimatedTime:String?
var coverImageView:String?
var equipment:[String]?
var instructions:[String]?
var isStarred:Bool?
}
My User struct:
struct User {
var userId:String?
var firstName:String?
var lastName:String?
var email:String?
}
My data model:
class MealPlanModel {
var delegate:MealPlanProtocol?
var listener:ListenerRegistration?
func getMealPlans(_ starredOnly:Bool = false) {
// Detach any listener
listener?.remove()
// Get a reference to the database
let db = Firestore.firestore()
var query:Query = db.collection("mealPlans")
// If filtering for starred Meal Plans, update the query
if starredOnly {
query = query.whereField("isStarred", isEqualTo: true)
}
self.listener = query.addSnapshotListener({ (snapshot, error) in
// Check for errors
if error == nil && snapshot != nil {
var mealPlans = [MealPlan]()
// Parse documents into mealPlans
for doc in snapshot!.documents {
let m = MealPlan(
docID: doc["docID"] as? String,
title: doc["title"] as! String,
recipeSource: doc["recipeSource"] as? String,
estimatedTime: doc["estimatedTime"] as? String,
coverImageView: doc["coverImageView"] as? String,
ingredientsProduce: doc["ingredientsProduce"] as? [String],
ingredientsProtein: doc["ingredientsProtein"] as? [String],
ingredientsSpices: doc["ingredientsSpices"] as? [String],
ingredientsOther: doc["ingredientsOther"] as? [String],
equipment: doc["equipment"] as? [String], instructions: doc["instructions"] as? [String],
isStarred: doc["isStarred"] as? Bool)
mealPlans.append(m)
}
// Call the delegate and pass back the notes in the main thread
DispatchQueue.main.async {
self.delegate?.mealPlansRetrieved(mealPlans: mealPlans)
}
}
})
}
func updateStarredStatus(_ docId:String, _ isStarred:Bool) {
let db = Firestore.firestore()
db.collection("mealPlans").document(docId).updateData(["isStarred":isStarred])
}
}
And the method for starring in my View Controller:
#IBAction func starButtonTapped(_ sender: Any) {
// Toggle the star filter status
isStarFiltered.toggle()
// Run the query
if isStarFiltered {
model.getMealPlans(true)
}
else {
model.getMealPlans()
}
// Update the starButton
setStarFilterButton()
}
Would it involve copying the docID of a starred MealPlan into a key in the Users struct? And then displaying those MealPlans when filtering for starred MealPlans?
Any help/guidance is much appreciated!
A solution is for each user to track their own meal plans. You could create a collection that stores the meal plan id's within the users document so it would look like this
meals
meal_0 //auto generated documentId
//meal ingredients
meal_1
//meal ingredients
meal_2
//meal ingredients
and then
users
user_0 //the documentId is the users uid
//user info
my_meals (collection)
meal_0: true
mean_2: true
That being said, it's often a good idea to keep data at a high level to reduce the amount of reads and simplify queries so this structure may be better instead of storing the users meals within the users document
starred_meals
user_0 //the documentId is the users uid
meal_0: true
mean_2: true
When a use logs in, read their document from the starred_meals collection.
To expand on that a little bit, sometimes a user will want to store other information with their meal so instead the above, how about this
starred_meals
user_0
starred_meal_0 (auto generated documentId)
meal_id: meal_0
wine_pairing: "Cabernet Sauvignon"
starred_meal_1
meal_id: meal_2
wine_pairing: "Barolo"
with that you could store all kinds of other information associated with each meal, per user

How to make firebase .observeSingleEventOf function run synchronously

I am running Firebase's .getSingleEventOf function to read data from my database in my program, and it is an asynchronous function. How would I make it synchronous (or make another synchronous function to house the code)?
I've tried to use the data passed through the function, but it hasn't been working! It only returns 0 elements, even though I know that I have data with the specific letters in my database.
import Foundation
import Firebase
struct CompeteUserFinderService {
static func findCompetitors(contains letters: String?) -> [String] {
//Make sure there was an input
guard let letters = letters else { return [] }
var usernames = [String]()
//Database reference
let ref = Database.database().reference().child("usernames")
ref.observeSingleEvent(of: .value) { (snapshot) in
//Creates an array of all values in the usernames branch
let value = Array((snapshot.value as! [String: Any]).keys)
usernames = value.map { $0.lowercased() }.filter { $0.contains(letters.lowercased()) }
}
return usernames
}
}

Cannot retrieve data from Firebase using Swift

As the title says I have a weird problem to retrieve simple data from Firebase, but I really can't figure out where I'd go wrong.
This is my schema:
And this the code:
import Firebase
let DB_BASE = Database.database().reference()
class FirebaseService {
static let instance = FirebaseService()
private var REF_BASE = DB_BASE
private var REF_SERVICE_STATUS = DB_BASE.child("Service_Status")
struct ServiceStatus {
var downloadStatus: Bool
var uploadStatus: Bool
}
func getServiceStatus() -> (ServiceStatus?) {
var serviceStatus: ServiceStatus?
REF_SERVICE_STATUS.observeSingleEvent(of: .value) { (requestSnapshot) in
if let unwrapped = requestSnapshot.children.allObjects as? [DataSnapshot] {
for status in unwrapped {
serviceStatus.downloadStatus = status.childSnapshot(forPath: "Download_Status").value as! Bool
serviceStatus.uploadStatus = status.childSnapshot(forPath: "Upload_Status").value as! Bool
}
// THANKS TO JAY FOR CORRECTION
return sponsorStatus
}
}
}
}
but at the end serviceStatus is nil. Any advice?
I think you may be able to simplify your code a bit to make it more manageable. Try this
let ssRef = DB_BASE.child("Service_Status")
ssRef.observeSingleEvent(of: .value) { snapshot in
let dict = snapshot.value as! [String: Any]
let down = dict["Download_Status"] ?? false
let up = dict["Upload_Status"] ?? false
}
the ?? will give the down and up vars a default value of false if the nodes are nil (i.e. don't exist)
Oh - and trying to return data from a Firebase asynchronous call (closure) isn't really going to work (as is).
Remember that normal functions propagate through code synchronously and then return a value to the calling function and that calling function then proceeds to the next line of code.
As soon as you call your Firebase function, your code is going to happily move on to the next line before Firebase has a chance to get the data from the server and populate the return var. In other words - don't do it.
There are always alternatives so check this link out
Run code only after asynchronous function finishes executing

Initialized variable: 'self' captured by a closure before all members were initialized

I'm having issues with initialization of a custom class. I need to set up an observer on some data when the class is initialized which updates properties. Initially, the properties can be empty and that is how I set them at initiation. However, Xcode still throws the "'self' captured by a closure before all members were initialized" error. Here is a shortened version of the code.
class Foo {
init() {
self.usersRef = ref.child("users")
self.usersRef.observe(DataEventType.value, with: { (snapshot) in
// snapshot error checking
// users is [String]
self.users = users
})
}
private var usersRef: DatabaseReference
private(set) var users: [String] = []
}
I have also tried
class Foo {
init() {
self.users = [String]()
self.usersRef = ref.child("users")
self.usersRef.observe(DataEventType.value, with: { (snapshot) in
// snapshot error checking
// users is [String]
self.users = users
})
}
private var usersRef: DatabaseReference
private(set) var users: [String]
}
to ensure initialization prior to the callback.
From this question it seemed I only needed to give the properties initial values, but this does not seem to be the case. I would prefer not to have to call another function to set up these handlers after initialization.
Thank you for an help
Try to use this block
{ [unowned self] (snapshot) in
// snapshot error checking
// users is [String]
self.users = users
})
or
{ [weak self] (snapshot) in
// snapshot error checking
// users is [String]
self?.users = users
})

Firebase Event listener doesn't work and I can't receive the right snapshot

I'm new here, please excuse me if I didn't manage to create a good
and well formatted question.
My problem is that I can't get a snapshot of a specific child in Firebase.
I need to put an event listener which will return me the value( the token of the user) when there is a change in the database:
notifications.child("tokens").child("id_user_token").child(user).
I also post an image of the database tree so you can see what's in there.
My purpose is to save the token retrieved from the Database and to create a HashMap(to build the notification) which will contain the token, a title and a message. As you can see from the code I pass all the parameters with the init() function. I try to call this function in a ViewController.swift like this:
"FirebaseMessagingService.init(user: "2", title: "Test",message: "Test")"
As you can see from the database image there is the user with id = 2 but when I try to print the snapshot it prints null and I can't understand what's wrong with the code.Image of the Database tree
Here is the exported JSON
import Foundation
import FirebaseMessaging
import FirebaseDatabase
class FirebaseMessagingService{
var mDatabase : DatabaseReference
var notifications = DatabaseReference()
init(user: String,title: String, message:String){
self.mDatabase = Database.database().reference()
self.notifications = Database.database().reference()
notifications = mDatabase.child("notificationRequests")
var notification = NSMapTable<AnyObject, AnyObject>()
notifications.child("tokens").child("id_user_token").child(user).observeSingleEven t(of: .value, with: { snapshot in
var token = Messaging.messaging().fcmToken! as String
print("\n\n\n\n\(snapshot)")
token = (snapshot.value as? String)!
notification.setValue(token, forKey: "toUser")
notification.setValue(title, forKey: "title")
notification.setValue(message, forKey: "message")
}){ (error) in
print("\n\n\n\n")
print(error.localizedDescription)
}
Try changing your code as follows
import FirebaseDatabase
class FirebaseMessagingService{
var ref: DatabaseReference!
init(user: String, title: String, message: String) {
self.ref = Database.database().reference()
let notifRef = self.ref.child("notificationRequests")
let thisUserRef = notifRef.child("tokens").child("id_user_token").child(user)
print(thisUserRef)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
}
It should at least, print thisUserRef which should be
yourDataBase/notificationRequests/tokens/id_user_token/2
It should also print the snapshot
snap( 2: cq3Xy...... )
Let us know what prints and if the paths are correct when it does print.
Also, in your question you stated your goal to be
I need to put an event listener which will return me the value( the
token of the user) when there is a change in the database:
The code your provided won't do that as it. You're using
.observeSingleEvent
Which does just that - observes a single event, one time. It will not leave a listener on the node for future events.
If you want to observe all events then use
.observe(.value... (or .childAdded etc)
However, I don't think you meant that you wanted to observe that specific node for changes (or maybe you did). You may have meant that when there is a change in the database elsewhere, you will then need to get the uid of that user, which the code in your question is trying to do.
Last thing:
You really shouldn't be defining this as a class. It should really be a function that is called like so within your viewController
class ViewController: UIViewController {
var ref: DatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = Database.database().reference()
self.showUid(user: "2", title: "some title", message: "some message")
}
func showUid( user: String, title: String, message: String) {
let notifRef = self.ref.child("notificationRequests")
let thisUserRef = notifRef.child("tokens").child("id_user_token").child(user)
print(thisUserRef)
thisUserRef.observeSingleEvent(of: .value, with: { snapshot in
print(snapshot)
})
}
}

Resources