How to get user's feed from Firebase? - ios

I have an iOS app that has a feed system basically like Facebook (With Firebase as server) and I'm trying to get the latest Posts from all the people that the user is following, but I can't find a way to loop through all data. Can I get a hand?
So this is more or less how my database is:
{
"Posts" : {
"Zlv1rX3p74R4G8DUvvBOcCiThcx1" : {
"-KacGJ-ps8FgCYvSag-D" : {
"latitud" : "37.33233141",
"longitud" : "-122.0312186",
"text" : "Party at my house",
"timestamp" : "1484586828.01879",
"ubicacionN" : "Apple Inc."
}
}
},
"following" : {
"Zlv1rX3p74R4G8DUvvBOcCiThcx1" : {
"Bdxaooy1HieFajZfZL8OeDFS1Q73" : {
"Uid" : "Bdxaooy1HieFajZfZL8OeDFS1Q73",
"handle" : "dnla",
"name" : "Daniela",
"profile_pic" : "https://firebasestorage.googleapis..."
}
}
}
}
I got the posts in one part and the list of following in another part (I'll add a complete pic of my DB at the end)
I'm not really familiar with NSDictionary so that's why I'm running into troubles, so I started doing this:
self.databaseRef.child("following").child(self.loggedInUser!.uid).observe(.childAdded, with: { (snapshot) in
//Getting the list of following
let snapshot = snapshot.value as? NSDictionary
self.following.append(snapshot)
}) { (error) in
print(error.localizedDescription)
}
for item in self.following {
Some code to get every user that the main user is following latests posts
}
but I'm hitting my head right now, some help please.
Picture explanation: Jalas = Posts

Related

Retrieving Data deep from Firebase Query (iOS, Swift 4)

So I have been developing my Job board application for a while now. I'm a total newbie to programming and swift. I wanted to allow my users to post jobs and view them and also apply for the jobs via the app. Similar to 'Freelancer' app. I am using Firebase for my database and the issue I have now is:
This is my Database Query :
"Users": {
"UserID" : {
"Region" : {
"autoID : {
"Job Title" : String
"Job Description" : String
"Company Name" : String
}
}
}
}
The thing is that I have to retrieve data from job details which is under autoID and UserID which always show an error - Cannot use multiple queryOrderedBy calls!
This is my retrieve function
func retrievePost() {
var ref: DatabaseReference!
ref = Database.database().reference()
let postDB = ref.child("Users")
postDB.queryOrdered(byChild: "Region").queryOrderedByKey().observeSingleEvent(of: .value) { (snapshot) in
let snapshotValue = snapshot.value as! [String:String]
let jobTitle = snapshotValue["Job Title"]!
let companyName = snapshotValue["Company Name"]!
let jobDesc = snapshotValue["Job Description"]!
let posted = post()
posted.jobTitle = jobTitle
posted.company = companyName
posted.jobDescription = jobDesc
self.jobsPosted.append(posted)
self.feedTable.reloadData()
}
}
The user should be able to post a job and other users should be able to view the jobs posted and also later edit and update it. Considering all these, What will be the solution ?
Will these be possible in firebase ?
Thanks in Advance!

Saving Offline data into CB Lite 2.0

issue : ( CB Lite 2.0 , Swift)
Registration screen which has name, email and dob and want to save in couchbase lite.
everytime user-filled form with these fields I want to store in local CB Lite DB
After that i want to fetch all record store in the cb lite.
Issue is :
Created a document
stored name , emal and dob in swift dictionary and tried to save and it stores but everytime i fetch it shows only one , not other user information.
Kinldy help me on this.
Raised Query on Couchbase Forum :
https://forums.couchbase.com/t/saving-offline-data-into-cb-lite-2-0/17877?u=st.shubh.tiwari
This is the way i called by taking one sample notification and checked its working :
let dict = [
"alert": "push notification.. (5)",
"badge": 1,
"sound": "default"
] as [String : Any]
var notificationArray = [Dictionary<String,Any>]()
let notiDict = CouchDBHelper.fetchDocumentByDocId(documentID: Constants.notificationDocument)
if let notiArray = notiDict["notification"] as? [Dictionary<String,Any>] {
notificationArray = notiArray
}
notificationArray.append(dict)
CCouchDBHelper.insertNotificationData(data: notificationArray, doc_id: Constants.notificationDocument)
objNotifcationSource = notificationArray
objNotificationTable.reloadData()
public func insertNotificationData(data:Any?,doc_id:String)
{
//let sharedInstance = CouchbaseAdapter.sharedInstance
let doc = MutableDocument(id: doc_id)
doc.setValue(data, forKey: "notification")
do {
try database.saveDocument(doc)
//try sharedInstance.database.saveDocument(doc)
print("Inserted Record :", data as! NSArray)
} catch let error as NSError {
print("Error in saving",error)
}
fetchDocumentByDocId(documentID: doc_id)
}

Listing Firebase data and relationships

I have just started working with Firebase database and I am a bit confused how to structure the database. In the following example I have a users object and a groups object. Each user can be part of multiple groups and every group can have multiple users. The proposed database structure is the following, according to "Structure Your Database".
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
"groups": {
"techpioneers": true,
"womentechmakers": true
}
}
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"startDate": "24-04-1820",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
}
}
}
Let's say I want to display all groups in a list in my app, with the group name and start date. How would I make that database call? Since the user object only contains the id of the groups, would I then have to make a separate call to the database for every group just to find out the name and start date of the group? If there are many groups in the list, then that becomes a lot of calls. My group might contain a lot of other information as well so this doesn't seem good for performance. Can I get all the groups in the groups list of the user, in one call?
One though I had was to include the name and start date in the groups object under the user:
"users": {
"alovelace": {
"name": "Ada Lovelace",
"groups": {
"techpioneers":{
"name": "Historical Tech Pioneers",
"startDate": "24-04-1820"
},
"womentechmakers":{
"name": "Women in Technology",
"startDate": "13-10-1823"
}
}
}
}
}
but this solution seems to add a lot of duplicate data. Also if I want to update the name I would have to do that in multiple locations. And maybe I want to add a sponsor organization object, that also contains group, and then want to list them. Then there would be 3 places to update the information on. How would I solve this?
You would then have two possibilities, one would be to store the data you need (duplicating it) in the groups node of each user.
The other, which is the one that I would recommend the most, would be to add an observeSingleEvent(of: .value) inside your first observer (that could be an observe(.value), observe(.childAdded) or whatever).
Say you have an array of all your group members, and an object named AppUser that represents a user in your app :
var groupMembers = [AppUser]()
To detect whenever a new member is added to a group for example, you could use a .childAdded observer for example :
func groupWasAddedObserver(completion: #escaping () -> ()) {
// Add a .childAdded observer to the group's members node (groupId should be defined somewhere) :
groupsRef.child(groupId).child("members").observe(.childAdded, with: { [weak self] (snapshot) in
// When you get the snapshot, store its key, which represents the member id :
let memberId = snapshot.key
// fetch this member's profile information using its id :
self?.getUser(memberId, completion: { (groupMember) in
// Once you got the profile information of this member, add it to an array of all your members for example :
self?.groupMembers.append(groupMember)
// Call the completion handler so that you can update the UI or reload a table view somewhere maybe depending on your needs :
completion()
})
})
}
And the second method to fetch a user data knowing his or her id :
func getUser(_ userId: String, completion: #escaping (AppUser) -> ()) {
// Add the observerSingleEvent observer :
usersRef.child(userId).observeSingleEvent(of: .value, with: { (snapshot) in
// Get the data you need using the snapshot you get :
guard let email = snapshot.childSnapshot(forPath: "email").value as? String else { return }
guard let name = snapshot.childSnapshot(forPath: "name").value as? String else { return }
guard let picUrl = snapshot.childSnapshot(forPath: "picUrl").value as? String else { return }
// Call the completion handler to return your user/member :
completion(AppUser(id: snapshot.key, email: email, name: name, picUrl: picUrl))
})
}
As you can see you get the memberId of each user using the snapshot key, and you use this memberId to fetch this specific user data.

Swift Firebase Completion Block Infinite Loop

I am making an app that holds many books in firebase. I am getting a very strange problem wherein my application will infinite loop when adding a new book and keep adding the same book as fast as it can. If there would be any way that a more experienced person could take a look, I would be very grateful.
#IBAction func userHasBook(sender: AnyObject) { // Called after filling a new book form
let email = FIRAuth.auth()?.currentUser?.email!
let school = email!.substringWithRange(Range(email!.characters.indexOf("#")!.advancedBy(1) ..< email!.characters.indexOf(".")!)) // for db organization
//A few lines here that ensure that the fields are filled correctly (clutter so i didn't add them)
ref.child(school).observeEventType(.Value, withBlock: { (snapshot) in
self.bookIndex = snapshot.value!["numSelling"] as! Int
self.addSellingBook(); // we now know it is done finding the value, right?
}) { (error) in
print(error.localizedDescription)
}
}
func addSellingBook(){
let bookRef = self.ref.child(school).child("selling").child(Int(self.bookIndex).description)
let book : [NSObject : AnyObject] = ["uid": (FIRAuth.auth()?.currentUser?.uid)!,
"title": self.titleField.text!,
"authors": self.authorsField.text!,
"edition": self.editionField.text!,
"price": self.priceField.text!,
"isbn" : self.isbn] // this is the data that is added infinitely many times
bookRef.updateChildValues(book, withCompletionBlock: { (NSError, FIRDatabaseReference) in //update the book in the db
let newIndex = self.bookIndex + 1
self.ref.child(self.school).child("numSelling").setValue(newIndex, withCompletionBlock: { (NSError, FIRDatabaseReference) in // after that update the index
self.performSegueWithIdentifier("backToMain", sender: nil) // and after that go back to main
})
})
Thanks a ton and ask me if you need anything more!
EDIT: JSON BEFORE BELOW
{
"colorado" : {
"numBuying" : 0,
"numSelling" : 0,
"users" : {
"2nU0jp4ITjgQ6ElSQWc7t5qj62t1" : {
"email" : "vhegde#colorado.edu"
}
}
},
"creek" : {
"numBuying" : 0,
"numSelling" : 2,
"selling" : [ {
"authors" : "A. S. A. Harrison",
"edition" : "Only Edition",
"isbn" : "1101608064",
"price" : "5.00",
"title" : "The Silent Wife",
"uid" : "eJvdVx3J8EYZPH3mlbYLBcPDkD12"
}, {
"authors" : "Jamie McGuire",
"edition" : "Only Edition",
"isbn" : "1476712050",
"price" : "5.00",
"title" : "Beautiful Disaster",
"uid" : "eJvdVx3J8EYZPH3mlbYLBcPDkD12"
} ],
"users" : {
"eJvdVx3J8EYZPH3mlbYLBcPDkD12" : {
"email" : "vhegde#creek.edu"
}
}
}
}
Then, I add another book (index of 2) and rather it keeps adding infinite books and infinitely increments index (numSelling). I don't want to post that JSON as it is like 300 lines long.
figured it out, instead of using observeEventType, you have to use observeSingleEventOfType
Please change Your code as i have implemented in below method to set online/offline.
// MARK: - User Online/Offline
func setUserOnlineOffline(userId: String!, isOnline: Bool!) {
Let ref = FIRDatabase.database().reference().child(FIRPATH_USER_USERID_DETAILS(userId))
if isOnline == true {
ref.updateChildValues(["last_seen": "Online"])
} else {
ref.updateChildValues(["last_seen": FIRServerValue.timestamp()])
}
ref.onDisconnectUpdateChildValues(["last_seen": FIRServerValue.timestamp()])
}
//note: here you have to set string, so please replace dictionary of nsobject with string .. might be helpful to you

firebase & swift - this class is not key value coding-compliant for the key -----

I know there are other questions like this but I think my problem is with how I'm accessing firebase and not an outlet because my error is in an #IBAction function that is able to be called before the error happens.
#IBAction func sign_in_out_tapped(sender : UIButton) {
if let op_user = user {
if (op_user.user_ref?.valueForKey("current_status"))! as! String == "OUT" {
let sign_in_time = NSDate()
op_user.user_ref?.childByAppendingPath("logins").updateChildValues([String(op_user.user_ref?.valueForKey("num_of_logins")! as! Int + 1): ["sign_in_time": sign_in_time]])
signinout = SignInOutModel(sign_in_time: sign_in_time)
op_user.user_ref?.updateChildValues(["current_status": "IN"])
} else {
signinout!.sign_out_time = NSDate()
op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).updateChildValues(["sign_out_time": signinout!.sign_out_time!])
signinout!.duration = (op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).valueForKey("sign_in_time")?.timeIntervalSinceNow)!
op_user.user_ref?.childByAppendingPath("logins").childByAppendingPath(String(user?.user_ref?.valueForKey("num_of_logins"))).updateChildValues(["duration": signinout!.duration])
op_user.user_ref?.updateChildValues(["total_hours": (Double((op_user.user_ref?.valueForKey("total_hours"))! as! NSNumber) + signinout!.duration)])
}
} else {
let sign_in_alert = UIAlertController(title: "Sign in.", message: "What is your first and last name?", preferredStyle: UIAlertControllerStyle.Alert)
sign_in_alert.addTextFieldWithConfigurationHandler { textField in
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(MainViewController.handleTextFieldTextDidChangeNotification(_:)), name: UITextFieldTextDidChangeNotification, object: textField)
}
func removeTextFieldObserver() {
NSNotificationCenter.defaultCenter().removeObserver(self, name: UITextFieldTextDidChangeNotification, object: sign_in_alert.textFields![0])
}
let cancel_action = UIAlertAction(title: "Cancel", style: .Cancel) { action in
print("Sign In Cancel Button Pressed")
removeTextFieldObserver()
}
let save_action = UIAlertAction(title: "Save", style: .Default) { action in
print("Sign Out Save Button Pressed")
let textField = sign_in_alert.textFields![0] as UITextField
if let user_name = textField.text {
var not_found = false
self.students_ref.childByAppendingPath(user_name).observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
not_found = true
} else {
self.user = User(user_name: user_name)
self.user?.user_ref = self.students_ref.childByAppendingPath(user_name)
self.refresh_user_name_label()
}
})
if !not_found {
self.mentors_ref.childByAppendingPath(user_name).observeEventType(.Value, withBlock: { snapshot in
if (snapshot.value is NSNull) {
not_found = true
} else {
self.user = User(user_name: user_name)
self.user?.user_ref = self.mentors_ref.childByAppendingPath(user_name)
self.refresh_user_name_label()
}
})
} else {
self.error_message("User not found. Please update Firebase.")
}
} else {
self.error_message("Could not sign in.")
}
removeTextFieldObserver()
}
save_action.enabled = false
AddAlertSaveAction = save_action
sign_in_alert.addAction(cancel_action)
sign_in_alert.addAction(save_action)
self.presentViewController(sign_in_alert, animated: true, completion: nil)
if let _ = user {
let sign_in_time = NSDate()
signinout = SignInOutModel(sign_in_time: sign_in_time)
}
}
refresh_sign_in_out_button()
}
I believe the error is at the top where it says "op_user.user_ref?.valueForKey("current_status"))! as String == "OUT"" because not only does the error say,
Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Firebase 0x7fa12e0b5530> valueForUndefinedKey:]: this class is not key value coding-compliant for the key current_status.'
but when going through the debugger, the program didn't terminate until "valueForKey("current_status")".
Any help would be appreciated! Thank you!
EDIT: My firebase:
{
"mentors" : {
"Ash Dreyer" : {
"current_status" : "IN",
"num_of_logins" : 0,
"total_hours" : 0
},
"Donald Pinckney" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Jasmine Zhou" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Michael Corsetto" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
}
},
"students" : {
"Bryton Moeller" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Kelly Ostrom" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Kyle Stachowicz" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
"Wesley Aptekar-Cassels" : {
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
}
}
}
EDIT:
The goal of my project is to create a sign in/out app. My mentor wants others to be able to see if someone is signed in or not and wants to track how long in total someone has been signed in (like if they have reached 100 hours at the shop or something.)
Your problem is the way you are trying to access the data of your Firebase reference. valueForKey isn't the right way to do it. You need to do one of two things:
First cast your firebaseSnap.value to a dictionary, and then use dict[key] syntax.
Snapshot.childSnapshotForPath("current_status").value
I would recommend number 2, as the cleanest way to do it. But if you need to access the reference a lot, you may want to cast to dictionary because accessing it will look nicer that way.
NOTE: Both of these require a firebase event listener to get the firebase snapshot.
Let me know if you have any questions, and good luck!
Based on your follow up comments, here's a few things that may help. Pardon the long-winded answer, but just trying to help reduce and simplify the amount of code you are writing.
In general, disassociating keys from data is a good model. The reason for that is: what if a user changes their name? They get married so a different last name, decide they like to be called Jim instead of James etc. If you use their name as the key, it's not changeable and anywhere else you refer to it will be stuck with that as well. If you make the name a child node, you can change it any time without affecting other data.
The first step is to change the structure to something like this
{
"mentors" : {
"uid_0" : {
"user_name": "Ash Dreyer",
"current_status" : "IN",
"num_of_logins" : 0,
"total_hours" : 0
},
"uid_1" : {
"user_name": "Donald Pinckney",
"current_status" : "OUT",
"num_of_logins" : 0,
"total_hours" : 0
},
The uid_0, uid_1 etc are created by Firebase when the user is created and can be retrieved from authData.uid. That's a great piece of data to use at the key for each user you store in your /mentors (or /users) node.
When the user signs in, you have a couple of options: if you don't need their data, you can just update the current_status node. You know the specific node name as it's their auth.uid.
let ref = /mentors node
let thisUser = ref.childByAppendingPath(auth.uid)
let thisUsersStatusRef = thisUser.childByAppendingPath("current_status")
thisUsersStatusRef.setValue("In")
and do the same thing for the timestamp.
If you need to display their name in the UI, just read their data via an observeSingleEvent, and update the current status and timestamp.
let ref = /mentors node
let thisUser = ref.childByAppendingPath(auth.uid)
ref.observeSingleEventOfType(.Value, withBlock: { snapshot
//read the user data from the snapshot and do whatever with it
let name = snapshot.value["user_name"] as? String
print(name!)
//now update the status
thisUsersStatusRef.setValue("In")
//and the timestamp
}
When the user signs out, all you need to do is the calculation for hours, set current_status to out and write the timestamp
thisUsersStatusRef.setValue("Out")
thisUsersHoursRef.setValue(hours)
thisUsersTimeStamp.setValue(timestamp)
The big picture here is that if you read in the user data when they log in, you have all of the information up front to calculate hours etc as it can be stored in a variable so when they log out, do the calculation and write. So it's essentially only hitting Firebaes twice (once when they log in and once when they log out).
The last thing is that each user should be observing the /mentors node for childAdded, childChanged or childRemoved events (NOT Value). With those observers, when a user is added, edited or changed, all of the users are notified with that single node of data, and can update their UI accordingly.

Resources