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.
Related
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()
I am manually entering in data into my database and the only variable not getting passed from my database is the author and I do not know where I am going wrong.
func getAllArticles(handler: #escaping (_ articles: [Article])-> ()){
var articleArray = [Article]()
REF_ARTICLES.observeSingleEvent(of: .value) { (articleMessageSnapshot) in
guard let articleMessageSnapshot = articleMessageSnapshot.children.allObjects as? [DataSnapshot] else {return}
for article in articleMessageSnapshot {
let content = article.childSnapshot(forPath: "content").value as? String ?? "no content"
let author = article.childSnapshot(forPath: "author").value as? String ?? "no author"
let twitterHandle = article.childSnapshot(forPath: "twitterHandle").value as? String ?? "none"
let articleTitle = article.childSnapshot(forPath: "articleTitle").value as? String ?? "no title"
let date = article.childSnapshot(forPath: "date").value as? String ?? "no date"
let article = Article(content: content, author: author, twitterHandle: twitterHandle, ArticleTitle: articleTitle, date: date)
articleArray.append(article)
}
handler(articleArray)
}
}
Please check out below code
var articleArray = [Article]()
//REF_ARTICLES
let ref = Database.database().reference().child(“articles”)
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let articleObj = Article()
articleObj.Content = dictionary["content"] as? String
articleObj.Author = dictionary["author"] as? String
articleObj.Twitterhandle = dictionary["twitterHandle"] as? String
articleObj.Title = dictionary["articleTitle"] as? String
articleObj.Date = dictionary["date"] as? String
self. articleArray.append(articleObj)
}, withCancel: nil)
}
I am also working on similar app where i am storing data to firebase and retrieving. Below approach i used to fetch the data from firebase database. Please try once.
func getAllArticles(handler: #escaping (_ articles: [Article])-> ()) {
Database.database().reference().child("Articles").observe(.childAdded, with: { (snapshot) in
print("articles = \(snapshot)")
if let dict = snapshot.value as? [String: Any] {
let article = Article()
article.articleTitle = dict["articleTitle"] as? String
article.author = dict["author"] as? String
article.twitterHandle = dict["twitterHandle"] as? String
article.date = dict["date"] as? String
article.content = dict["content"] as? String
self.articleArray.append(article)
}
handler(articleArray)
}, withCancel: nil)
}
im not sure what the underlying issue was, but i fixed it by deleting "author" from the firebase tree and then adding it back
I have this function that it will be triggered every time a user changes his picture, and will get the profilePictureUrl of the current user and update all posts matching the currentUser in the database. Works fine! Is doing the job. How do I update the indexPath of those particular cells? Right now my cells images are empty after the update.
var pathToPictures = [Pictures]()
func updateAllImagesOfCurrentUserInDatabase() {
//refrences
let ref = FIRDatabase.database().reference()
let uid = FIRAuth.auth()?.currentUser?.uid
//match the user ID value stored into posts with current userID and get all the posts of the user
let update = ref.child("posts").queryOrdered(byChild: "userID").queryEqual(toValue: uid)
update.observe(FIRDataEventType.value, with: { (snapshot) in
self.pathToPictures.removeAll()
if snapshot.value as? [String : AnyObject] != nil {
let results = snapshot.value as! [String : AnyObject]
for (_, value) in results {
let pathToPostPicture = Pictures()
if let pathToImage = value["pathToImage"] as? String , let postID = value["postID"] as? String {
pathToPostPicture.postImageUrl = pathToImage
pathToPostPicture.postID = postID
self.pathToPictures.append(pathToPostPicture)
print("Image and POST ID: \(pathToPostPicture.postImageUrl!)")
print("Post ID is : \(postID)")
if FIRAuth.auth()?.currentUser?.uid == uid {
ref.child("Users_Details").child(uid!).child("profileImageUrl").observeSingleEvent(of: .value, with: { (userSnapshot) in
print(userSnapshot)
let userIMageUrl = userSnapshot.value as! String
pathToPostPicture.postImageUrl = userIMageUrl
self.pathToPictures.append(pathToPostPicture)
print("This is the image path:" + userIMageUrl + String(self.pathToPictures.count))
// Update the Database
let postIDCount = pathToPostPicture.postID!
let updatedUserData = ["posts/\(postIDCount)/pathToImage": pathToPostPicture.postImageUrl!]
print("This is THE DATA:" , updatedUserData)
ref.updateChildValues(updatedUserData as Any as! [AnyHashable : Any])
})
}
print(self.pathToPictures.count)
}
}
} else {
print("snapshot is nill - add some data")
}
self.collectionView?.reloadData()
})
}
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.
My app has a collection view which displays rows of photos/images from Firebase, and I'd like to have them load in the order they were added, with the newest posts at the top. I thought that using queryOrderedByKey did that by default, and that's what I used in my fetch function, but the posts are out of order.
This is how I'm fetching the posts currently:
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
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 poster = post["poster"] as? String, let likes = post["likes"] as? Int, let pathToImage = post["pathToImage"] as? String, let postID = post["postID"] as? String {
posst.poster = poster
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)
}
}
posts.append(posst)
}
}
}
self.collectionView.reloadData()
}
}
})
ref.removeAllObservers()
}
}
}
})
}
How can I get the posts to sort by newest first?
EDIT 2: updated - now sorting from oldest -> newest
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 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()
}
}
}
})
}
Queries return a snapshot the matching child nodes in the order requested. That means that the results consists of three things:
the item keys
the item values
the order of the items relative to each other
But then the first thing you do with the snapshot is:
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snap) in
let postsSnap = snap.value as! [String : AnyObject]
You convert the snapshot into a dictionary. And a dictionary only has space for two things: the keys and the values. So the order is lost at this point, since dictionaries have an undefined order.
The proper way to access the result in the order you requested is to use the snapshot's built-in collection of children to iterate over them:
ref.child("posts").queryOrderedByKey().observeSingleEvent(of: .value, with: { (snapshot) in
for postSnapshot in snapshot.children {
let value = postSnapshot.value as! [String : AnyObject]
This will loop over the matching children in the order you queries them.