I am trying to add a custom branch that lists all friends for a user. Currently, I have the code to add a user's email and name (see user1 in the photo below).
let ref = Database.database().reference(fromURL: FIREBASE_GAME)
let usersReference = ref.child("users").child(uid)
let values = ["name" : name, "email" : email]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if(err != nil) {
print(err!)
return
}
Would I add "friends" as a key in my values dictionary and then let the value be a sub-branch? I don't know what that would look like in code. I made user2 manually in Firebase (this is what I want to do in code). I'm new to Firebase and most resources give very basic examples. I would appreciate any help.
I think you're looking for this:
let values = ["name" : name, "email" : email, "friends": [ "jake": false, "mike": true ]]
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if(err != nil) {
print(err!)
return
}
Alternatively, you can write/update the friends with a separate call:
usersReference.child("friends").updateChildValues(["jake": false, "mike": true])
Create a Model Class in Below Format. You need to change the object Friend in your Firebase Console.
class ListModel: NSObject {
var UID:String?
var Name:String?
var Email:String?
var Friends:[Friend]?
}
class Friend : NSObject {
var Name : String?
var Status : Bool?
}
In Your View Controller you can access using below Code
var Obj : ListModel?
var ListArr = [ListModel]()
let ref = Database.database().reference().child(“users”)
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject]
else {
return
}
let Obj = ListModel()
Obj.UID = snapshot.key
Obj.Name = dictionary["name"] as? String
Obj.Email = dictionary["email"] as? String
let friendsArr = dictionary[“friends”] as? NSArray
var friendObj = [Friend]()
if friendsArr != nil{
for dict in friendsArr as! [[String: AnyObject]] {
let friend = Friend)
friend.Name = dict["name"] as? String
friend.Status = dict["mobile"] as? Bool
friendObj.append(friend)
}
}
Obj. friendsArr = friendObj
self.ListArr.append(Obj)
}, withCancel: nil)
Related
I'm having a hard time trying to retrieve both the postId and commentId at the same time.
The goal is to allow a user to delete their own comment on a post.
Here is the Comment Struct, below is a the function that should delete the users comment, however for some reason I can only return the commentId and not the postId. So the comment is not deleted when clicked.
The postId will = nil if I run a break point in the final like of deleteComment, but if I run a break point on the final line of func fetchComment the postId will return whatever the postId is for the post.
struct Comment {
var commentId: String!
let user: User
var creationDate: Date!
let text: String
let uid: String!
init(commentId: String!,user: User, dictionary: [String: Any]) {
self.commentId = commentId
self.user = user
self.text = dictionary["text"] as? String ?? ""
self.uid = dictionary["uid"] as? String ?? ""
if let creationDate = dictionary["creationDate"] as? Double {
self.creationDate = Date(timeIntervalSince1970: creationDate)
}
}
var post: Post?
func deleteComment() {
guard let postId = self.post?.postId else { return }
guard let commentId = self.commentId else { return }
let commentsRef = Database.database().reference().child("comments")
commentsRef.child(postId).child(commentId).removeValue()
}
Here is how comments are fetched
var comments = [Comment]()
func fetchComments() {
guard let postId = self.post?.postId else { return }
let ref = Database.database().reference().child("comments").child(postId)
ref.observe(.childAdded, with: { (snapshot) in
let commentId = snapshot.key
//print(commentId)
guard let dictionary = snapshot.value as? [String: Any] else { return }
guard let uid = dictionary["uid"] as? String else { return }
Database.fetchUserWithUID(with: uid, completion: { (user) in
let comment = Comment(commentId: commentId, user: user, dictionary: dictionary)
self.comments.append(comment)
self.collectionView?.reloadData()
})
}) { (err) in
print("Failed to observe comments")
}
}
Also, here is the code when users submit a comment
func didSubmit(for comment: String) {
guard let uid = Auth.auth().currentUser?.uid else { return }
print("post id:", self.post?.postId ?? "")
print("Inserting comment:", comment)
let postId = self.post?.postId ?? ""
let values = ["text": comment, "creationDate": Date().timeIntervalSince1970, "uid": uid] as [String : Any]
Database.database().reference().child("comments").child(postId).childByAutoId().updateChildValues(values) { (err, ref) in
if let err = err {
print("Failed to insert comment:", err)
return
}
self.uploadCommentNotificationToServer()
if comment.contains("#") {
self.uploadMentionNotification(forPostId: postId, withText: comment, isForComment: true)
}
self.containerView.clearCommentTextView()
}
}
Json for comments via firebase
"comments" : {
"-Lord0UWPkh5YdGIHtAO" : {
"-Lp7AQzHccme5RcRsyDd" : {
"creationDate" : 1.568874020882821E9,
"text" : "Wow Cool!",
"uid" : "wELwYnMGxxW0LJNRKBuuE2BUaV93"
}
},
"-LowvCk-agvJbK0VF-Bq" : {
"-LpKm1NcgOsXhwj6soxE" : {
"creationDate" : 1.569102243436777E9,
"text" : "Nice!",
"uid" : "wELwYnMGxxW0LJNRKBuuE2BUaV93"
},
One option is to make postId a property in the Comment struct and add it to the init method, this way you will always has access to it
struct Comment {
let postId: String
var commentId: String!
let user: User
var creationDate: Date!
let text: String
let uid: String!
init(commentId: String!,user: User, postIdentifier: String, dictionary: [String: Any]) {
self.commentId = commentId
self.user = user
self.postId = postIdentifier
...
I am trying to allow users to delete comments upon pressing the delete button. When comments are submitted, they're created using autoId and the header of the node will be the postId to see what post they commented on.
"comments" : {
"-LmfZZis5ovtBwfm_4xR" : {
"-LoHu5Qv3BmuHTsSlthj" : {
"creationDate" : 1.567980283717026E9,
"text" : "Kkkk",
"uid" : "64r3dgTN6xMhHYhptFlsFWX0dLk2"
},
"-LoHuPohuQ3eUtDWL_G-" : {
"creationDate" : 1.567980367209054E9,
"text" : " Ok",
"uid" : "64r3dgTN6xMhHYhptFlsFWX0dLk2"
}
}
},
I do not know how to retrieve the autoId so current logged in users can delete their comments. Here is the code for submission
func didSubmit(for comment: String) {
guard let uid = Auth.auth().currentUser?.uid else { return }
print("post id:", self.post?.postId ?? "")
print("Inserting comment:", comment)
let postId = self.post?.postId ?? ""
let values = ["text": comment, "creationDate": Date().timeIntervalSince1970, "uid": uid] as [String : Any]
Database.database().reference().child("comments").child(postId).childByAutoId().updateChildValues(values) { (err, ref) in
if let err = err {
print("Failed to insert comment:", err)
return
}
self.uploadCommentNotificationToServer()
if comment.contains("#") {
self.uploadMentionNotification(forPostId: postId, withText: comment, isForComment: true)
}
self.containerView.clearCommentTextView()
}
}
Comment struct
struct Comment {
var commentId: String!
let user: User
var creationDate: Date!
let text: String
let uid: String!
init(commentId: String!,user: User, dictionary: [String: Any]) {
self.commentId = commentId
self.user = user
self.text = dictionary["text"] as? String ?? ""
self.uid = dictionary["uid"] as? String ?? ""
if let creationDate = dictionary["creationDate"] as? Double {
self.creationDate = Date(timeIntervalSince1970: creationDate)
}
}
var post: Post?
func deleteComment() {
guard let postId = self.post?.postId else { return }
let commentsRef = Database.database().reference().child("comments")
commentsRef.child(postId).child(commentId).removeValue()
}
}
Code to fetch the comments
var comments = [Comment]()
func fetchComments() {
guard let postId = self.post?.postId else { return }
let ref = Database.database().reference().child("comments").child(postId)
ref.observe(.childAdded, with: { (snapshot) in
let commentId = snapshot.key
guard let dictionary = snapshot.value as? [String: Any] else { return }
guard let uid = dictionary["uid"] as? String else { return }
Database.fetchUserWithUID(with: uid, completion: { (user) in
let comment = Comment(commentId: commentId, user: user, dictionary: dictionary)
self.comments.append(comment)
self.collectionView?.reloadData()
})
}) { (err) in
print("Failed to observe comments")
}
}
Thanks!
To be able to delete a node, you must know the full path to that node.
There are two ways to know the key of the product you want to delete:
You've passed it along your app from the moment when you loaded the data.
You have some other value that allows you to perform a query on the database to look up the key.
The first option is the most common, as you'll typically be loading the data from the database already to display it to the user. In that case you should "simply" pass the key of the data along when displaying the value.
Once you have the key of the product/child node, you can delete it with:
let postId = "-LmfZZis5ovtBwfm_4xR"
let commentId = "-LoHu5Qv3BmuHTsSlthj"
let commentsRef = Database.database().reference().child("comments")
commentsRef.child(postId).child(commentId).removeValue()
does anyone have a suggestion how to get rid off the user obligation when posting new element to the Firebase database? I would like to create open feed but this is only for one particular user.
Thank you!
The function starts like:
func fetchPosts(){
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot inlet users = snapshot.value as! [String : AnyObject]
The main problem I see in this:
for (_,value) in users {
if let uid = value["uid"] as? String {
if uid == Auth.auth().currentUser?.uid {
if let followingUsers = value["following"] as? [String : String]{
for (_,user) in followingUsers{
self.following.append(user)
}
}
self.following.append(Auth.auth().currentUser!.uid)
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in
let postsSnap = snap.value as! [String : AnyObject]
for (_,post) in postsSnap {
if let userID = post["userID"] as? String {
for each in self.following {
if each == userID {
I guess this would be the same:
let posst = Post()
if let author = post["author"] as? String, let likes = post["likes"] as? Int, let pathToImage = post["pathToImage"] as? String, let postID = post["postID"] as? String {
posst.author = author
posst.likes = likes
posst.pathToImage = pathToImage
posst.postID = postID
posst.userID = userID
if let people = post["peopleWhoLike"] as? [String : AnyObject] {
for (_,person) in people {
posst.peopleWhoLike.append(person as! String)
}
}
self.posts.append(posst)
}
}
}
self.collectionview.reloadData()
}
}
})
}
}
}
})}
OK so if I am understanding your question correctly you want anyone signed in to be able to see all the posts not just the posts of the users they are following.
If this is in fact the case then you do not need to check who the user is following. You just want to load all of the posts. Change
for (_,post) in postsSnap {
if let userID = post["userID"] as? String {
for each in self.following {
if each == userID {
To
self.ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { snap in
if let postsSnap = snap.value as? [String: AnyObject] {
for(_, post) in postsSnap {
if let userID = post["userID"] as? String {
for each in self.following {
if each == userID {
let posst = Post()
if let author = post["author"] as? String, let likes = post["likes"] as? Int, let pathToImage = post["pathToImage"] as? String, let postID = post["postID"] as? String, let postDescription = post["postDescription"] as? String, let timestamp = post["timestamp"] as? Double, let category = post["category"] as? String, let group = post["group"] as? Int {
posst.author = author
posst.likes = likes
posst.pathToImage = pathToImage
posst.postID = postID
posst.userID = userID
posst.fancyPostDescription = self.createAttributedString(author: author, postText: postDescription)
posst.postDescription = author + ": " + postDescription
posst.timestamp = timestamp
posst.group = group
posst.category = category
posst.userWhoPostedLabel = self.createAttributedPostLabel(username: author, table: group, category: category)
if let people = post["peopleWhoLike"] as? [String: AnyObject] {
for(_, person) in people {
posst.peopleWhoLike.append(person as! String)
}
}
self.posts.append(posst)
} // end if let
}
}
self.tableView.reloadData()
}
}
}
})
ref.removeAllObservers()
If you want anyone to be able to read / write to your database you have to update the rules in the Firebase console. You do this by clicking on database then rules. The default rules look like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
Change them to
{
"rules": {
".read": "true",
".write": "true"
}
}
I looked at the video you're following and he make's a lot of mistakes. Really when you request data from Firebase you should be paging i.e. loading a set number of posts and then loading more based on the scroll position.
I would like to get some data in user profile, I've got some info but I got stuck at this one.
Firebase database:
Here is some of my code:
self.ref.child("users").child("profile").observe(.childAdded, with: { (snapshot) in
if let dictionary = snapshot.value as? [String:Any] {
let user = User()
let brands = dictionary["status"] as! NSDictionary
user.displayname = dictionary["displayname"] as? String
user.isconnected = brands["isconnected"] as? String
print(user.isconnected) //fatal error: unexpectedly found nil while unwrapping an Optional value
class User: NSObject {
var displayname: String?
var isconnected: String?
}
Try something like that :
guard let dictionary = snapshot.value as? [String: Any] else {
return
}
guard let statusDictionary = dictionary["status"] as? [String: Any] else {
return
}
guard let deviceStatusDictionary = statusDictionary["DEVICE_KEY"] as? [String: Any] else {
return
}
let user = User()
user.displayname = dictionary["displayname"] as? String
user.isconnected = deviceStatusDictionary["isconnected"] as? Bool
You need to know your device key.
And :
class User: NSObject {
var displayname: String?
var isconnected: Bool?
}
I'm trying to make a fetch from my database to populate a collection view, in order of newest at the top, down to oldest. I tried using snap.children.allObjects.reversed(), but my app crashes upon loading. Here's the full fetch function:
func fetchPosts() {
let ref = FIRDatabase.database().reference()
ref.child("users").queryOrderedByKey().observe(.value, with: { snapshot in
let users = snapshot.value as! [String : AnyObject]
for (_, value) in users {
if let uid = value["uid"] as? String {
if uid == FIRAuth.auth()?.currentUser?.uid {
if let followingUsers = value["following"] as? [String : String] {
for (_, user) in followingUsers {
self.following.append(user)
}
}
self.following.append(FIRAuth.auth()!.currentUser!.uid)
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in
for postSnapshot in snap.children.allObjects.reversed() as! [FIRDataSnapshot] {
let value = postSnapshot.value as! [String : AnyObject]
if let userID = value["userID"] as? String {
for each in self.following {
if each == userID {
let posst = Post()
if let poster = value["poster"] as? String, let likes = value["likes"] as? Int, let pathToImage = value["pathToImage"] as? String, let postID = value["postID"] as? String {
posst.poster = poster
posst.likes = likes
posst.pathToImage = pathToImage
posst.postID = postID
posst.userID = userID
if let people = value["peopleWhoLike"] as? [String : AnyObject] {
for (_, person) in people {
posst.peopleWhoLike.append(person as! String)
}
}
posts.append(posst)
}
}
}
self.collectionView.reloadData()
}
}
})
ref.removeAllObservers()
}
}
}
})
}
The error is EXC_BAD_INSTRUCTION (code=EXC_1386_INVOP, subcode=0x0), with the warning Cast from 'ReversedRandomAccessCollection<[Any]>' (aka 'ReversedRandomAccessCollection>') to unrelated type '[FIRDataSnapshot]' always fails.
Is .reversed not the way to go about this? As it is, my code without .reversed loads the posts in order from oldest at the top, down to the newest at the bottom. How can I switch it around?
EDIT: Firebase snippet of posts:
"posts" : {
"-KfWzWv8rP38bUreDupj" : {
"likes" : 1,
"pathToImage" : "https://firebasestorage.googleapis.com/v0/b/cloudcamerattt.appspot.com/o/posts%2F1JSgke8QqFds4CxF2Z4MhuzbRoW2%2F-KfWzWv8rP38bUreDupj.jpg?alt=media&token=fef86bea-1ae2-4e1e-82fa-6209bc281a5e",
"peopleWhoLike" : {
"-KfX29jTcwaQDpkdIVX8" : "yI6NokUl2mTa7Uah4SgtAiulTJH2",
"-KfXQJBRemZUCI2ieT94" : "MpnGvQj7ZOdz12zKD0bTeX1kp0B3"
},
"postID" : "-KfWzWv8rP38bUreDupj",
"poster" : "Harry Potter",
"userID" : "1JSgke8QqFds4CxF2Z4MhuzbRoW2"
},
EDIT 2: Adding a timestamp
Added var timestamp: Int! to my Post object, then add it into my upload function:
func uploadToFirebase() {
AppDelegate.instance().showActivityIndicator()
let uid = FIRAuth.auth()!.currentUser!.uid
let ref = FIRDatabase.database().reference()
let storage = FIRStorage.storage().reference(forURL: "gs://cloudcamerattt.appspot.com")
let key = ref.child("posts").childByAutoId().key
let imageRef = storage.child("posts").child(uid).child("\(key).jpg")
let data = UIImageJPEGRepresentation(self.previewImage.image!, 0.6)
var Timestamp: TimeInterval {
return NSDate().timeIntervalSince1970 * 1000
}
let uploadTask = imageRef.put(data!, metadata: nil) { (metadata, error) in
if error != nil {
print(error!.localizedDescription)
AppDelegate.instance().dismissActivityIndicator()
return
}
imageRef.downloadURL(completion: { (url, error) in
if let url = url {
let feed = ["userID" : uid,
"pathToImage" : url.absoluteString,
"likes" : 0,
"poster" : FIRAuth.auth()!.currentUser!.displayName!,
"postID" : key,
"timestamp" : (0-Timestamp)] as [String : Any]
let postFeed = ["\(key)" : feed]
ref.child("posts").updateChildValues(postFeed)
AppDelegate.instance().dismissActivityIndicator()
let feedController = self.storyboard?.instantiateViewController(withIdentifier: "feedVC") as! FeedViewController
feedController.navigationItem.setHidesBackButton(true, animated: false)
self.tabBarController?.selectedIndex = 0
}
})
}
uploadTask.resume()
}
Then add it into my fetch:
let posst = Post()
if let poster = value["poster"] as? String, let likes = value["likes"] as? Int, let pathToImage = value["pathToImage"] as? String, let postID = value["postID"] as? String, let timestamp = value["timestamp"] as? Int {
posst.poster = poster
posst.likes = likes
posst.pathToImage = pathToImage
posst.postID = postID
posst.userID = userID
posst.timestamp = timestamp
Updated fetch function (results in crash Could not cast value of type 'FIRDataSnapshot' (0x10584eee8) to 'NSArray' (0x107b43dd8).):
func fetchPosts() {
let ref = FIRDatabase.database().reference()
ref.child("users").queryOrderedByKey().observe(.value, with: { snapshot in
let users = snapshot.value as! [String : AnyObject]
for (_, value) in users {
if let uid = value["uid"] as? String {
if uid == FIRAuth.auth()?.currentUser?.uid {
if let followingUsers = value["following"] as? [String : String] {
for (_, user) in followingUsers {
self.following.append(user)
}
}
self.following.append(FIRAuth.auth()!.currentUser!.uid)
for child in snapshot.children.reversed() {
let snap = child as! [FIRDataSnapshot]
ref.child("posts").queryOrdered(byChild: "timestamp").observeSingleEvent(of: .value, with: { (snap) in
if let userID = value["userID"] as? String {
for each in self.following {
if each == userID {
let posst = Post()
if let poster = value["poster"] as? String, let likes = value["likes"] as? Int, let pathToImage = value["pathToImage"] as? String, let postID = value["postID"] as? String, let timestamp = value["timestamp"] as? Int {
posst.poster = poster
posst.likes = likes
posst.pathToImage = pathToImage
posst.postID = postID
posst.userID = userID
posst.timestamp = timestamp
if let people = value["peopleWhoLike"] as? [String : AnyObject] {
for (_, person) in people {
posst.peopleWhoLike.append(person as! String)
}
}
posts.append(posst)
}
}
}
self.collectionView.reloadData()
}
})
}
ref.removeAllObservers()
}
}
}
})
}
Try
for child in snapshot.children.reversed() {
let snap = child as! FIRDataSnapshot
print(snap)
}
You are ordering by key which will load the oldest to the newest. If you want to reverse the order, and let Firebase do the heavy lifting, use a technique for reverse chronological order posted here
In Firebase, how can I query the most recent 10 child nodes?
Then it's easy to do a reverse query...
"posts" : {
"-KfWzWv8rP38bUreDupj" : {
"likes" : 1,
"pathToImage" : "https:/...",
"peopleWhoLike" : {
"-KfX29jTcwaQDpkdIVX8" : "yI6NokUl2mTa7Uah4SgtAiulTJH2",
"-KfXQJBRemZUCI2ieT94" : "MpnGvQj7ZOdz12zKD0bTeX1kp0B3"
},
"postID" : "-KfWzWv8rP38bUreDupj",
"poster" : "Harry Potter",
"timestamp" : -1.46081635550362E12, //Just add this child
"userID" : "1JSgke8QqFds4CxF2Z4MhuzbRoW2"
},
and then
ref.child("posts").queryOrdered(byChild: "timestamp").observe(...
Also, the duplicate postID is probably not needed as a child as it's the key to the post as well.
You are correct that you need to cast something somewhere, because Swift only knows that we started with an array of Any. The problem is that you are casting the wrong thing in the wrong place. Cast postSnapshot at the start of the inside of the for loop.
The way to figure out this sort of thing is to make a simplified playground example. You are doing the equivalent of this:
let arr : [Any] = [1,2,3]
for i in arr.reversed() as! [Int] { // crash
}
What we know in that example, however, is not something about arr.reversed(); it is that i is an Int. This is fine:
let arr : [Any] = [1,2,3]
for i in arr.reversed() {
if let i = i as? Int {
// now it is safe to use `i`
}
}
Your case is parallel. At the start of the for loop, you need to cast postSnapshot to a FIRDataSnapshot. Now you can proceed.