posts disappearing from feed when like button is pressed - swift

I am using the following function to update posts in my feed:
func fetchPosts() {
let query = COLLECTION_POSTS
.order(by: "timestamp", descending: true)
query.addSnapshotListener { snapshot, _ in
guard let changes = snapshot?.documentChanges.filter({$0.type == .added}) else {return}
self.posts = changes.compactMap {
do {
return try $0.document.data(as: Post.self)
} catch {
print("Error converting Firestore document to Post object: \(error)")
return nil
}
}
self.fetchUserPosts()
}
}
When I like a post and update the like count the posts disappear from the feed, then I get a debug message via the self.fetchuserposts() function that there are no posts on the database. However, despite this, the likes still increase on the server side.
The following is the code that updates the like field when a user presses it:
func likePost(post: Post) {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let postId = post.id else { return }
let userLikesRef = Firestore.firestore().collection("users").document(uid).collection("user-likes")
Firestore.firestore().collection("posts").document(postId)
.updateData(["likes" : post.likes + 1]) { _ in
userLikesRef.document(postId).setData([:]) { _ in
self.isLiked = true
}
}
}
I have tried many different methods of updating the like field but I cannot seem to figure it out. I expect that there is maybe an issue where the model is not being updated but that does not make sense to me given that the snapshot listener is listening for changes.

I (jankily) fixed this issue using the folllowing code. However, all posts in the feed reload when pressing the like button, not ideal, I would still appreciate help if anyone has a better solution!
func fetchPosts() {
let query = COLLECTION_POSTS
.order(by: "timestamp", descending: true)
query.addSnapshotListener { snapshot, _ in
guard let changes = snapshot?.documentChanges else {return}
for change in changes {
if change.type == .added {
do {
let post = try change.document.data(as: Post.self)
self.postsArray.append(post)
} catch {
print("Error converting Firestore document to Post object: \(error)")
}
}
}
self.posts = self.postsArray
self.fetchUserPosts()
}
}

Related

How to detect if there is no more post to fetch in CollectionView?

I am making new app with Xcode using Swift and i am fetching posts from my WordPress website , all works fine but there is one problem, when i scroll down to the very last post of category then the indicator is just running and nothing happens, i want when there is no more post then Progress bar should stop running and i want to toast a message that there is no more post , how is that possible ? this is my code to fetch posts
func fetchPostData(completionHandler: #escaping ([Postimage]) -> Void ) {
let url = URL(string: "https://www.sikhnama.com/wp-json/wp/v2/posts/?categories=4&page=\(page)\(sortBy)")!
print(url)
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else {return}
do {
let postsData = try JSONDecoder().decode([Postimage].self, from: data)
completionHandler(postsData)
DispatchQueue.main.async {
if(self.newsData.isEmpty == false){
print("collection view empty")
self.collectionView.reloadData()
SVProgressHUD.dismiss()
}
else{
if(self.collectionView == nil){
print("collection view nill")
self.fetchPostData { (posts) in
self.newsData = posts }
}
}
}
}
catch {
let error = error
print(String(describing: error))
}
}
task.resume()
}
can you please help ?

How can I fix "Argument passed to call that takes no arguments" on function in Swift using FirebaseFirestoreSwift?

func fetchUser() {
guard let uid = userSession?.uid else { return }
Firestore.firestore().collection("users").document(uid).getDocument { snapshot, _ in
guard let user = try? snapshot?.data(as: User.self) else { return }
self.currentUser = user
}
}
The error is thrown on the "guard let user" line. Any idea on how to fix this error? Also, I am not using Cocoapods ... I am using Firebase from the github sdk
You need to ensure that you have:
import FirebaseFirestoreSwift
With this the data(as: ) function, it should work.
it seems you are missing the "documents" step, could you try something like this:
func fetchUser() {
guard let uid = userSession?.uid else { return }
Firestore.firestore().collection("users").document(uid).getDocument { snapshot, _ in
guard let docs = snapshot else { return }
for doc in docs.documents {
let user = doc.data(as: User.self)
self.currentUser = user
// break as required or just get the first in documents
}
}
}

Swift: upload multiple images to Firestore and assign URLs to object

I'm making an app whereby users post 2 images. I'm using Firebase for storage and as my database.
In my method to upload the images what I had wanted to do was to essentially use this method to return the URLs separately as well. I had written the following:
private func uploadImage(image: UIImage) -> URL? {
let randomName = UUID()
let storageRef = storage.reference().child("\(randomName)/png")
guard let uploadData = image.pngData() else { return nil}
var imageUrl: URL?
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
if error != nil {
print(error?.localizedDescription)
} else {
imageUrl = url
}
}
}
return imageUrl
}
And then I wrote the following 'post' method which is run when the submit button is tapped:
#objc func post() {
if let question = questionText.text,
let hashtagText = hashtagTextField.text,
let userHandle = Auth.auth().currentUser?.email,
let firstImage = left.image,
let secondImage = right.image,
let firstImageURL = uploadImage(image: firstImage)?.absoluteString,
let secondImageURL = uploadImage(image: secondImage)?.absoluteString
{
db.collection("posts").addDocument(data: [
"firstImage" : firstImageURL,
"secondImage" : secondImageURL,
"question" : question,
"hashtagText" : hashtagText,
"userHandle" : userHandle
]) { (error) in
if let e = error {
print("There was an issue saving data to Firestore, \(e)")
} else {
print("Successfully saved data")
self.dismiss(animated: true, completion: nil)
}
}
}
}
However, obviously the first method is not going to work as the closure is run after imageUrl is returned, therefore returning nil.
I've been trying to figure out how to manage this scenario - I had considered using a loop to populate an array of images but this got messy and I'm sure it is not the standard way to handle this. Any help would be greatly appreciated.
The return imageUrl is in the wrong place. It will return before Firebase has had time to store the image and return the url.
Additionally, the name of the file is not going to work. You currently have
storage.reference().child("\(randomName)/png") // xxxxx/png?
when it should be
storage.reference().child("\(randomName).png") // xxxxx.png
You can't 'return' data from a Firebase closure because firebase is asynchronous - a completion handler may possibly be a solution, but we don't know what the total use case is.
Let's assume you want want to store a users vacation picture in storage and then store that url in Firestore
private func uploadImage(image: UIImage) {
guard let uid = Auth.auth().currentUser?.uid else { return } //this users uid
let storageRef = storage.reference().child(uid).child("vacation.png")
//the path will be storage/users uid/vacation.png
guard let uploadData = image.pngData() else { return nil}
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if url != nil {
//it's here where we store the imageUrl in Firestore
let dict = ["theUrl": url?.absoluteURL)]
let userRef = self.db.collection("users").document(uid)
//self.db points to *my* Firestore
userRef.collection("my_pics").addDocument(data: dict)
//will store in firstore/users/uid/docId/theUrl: the url
}
}
}
}
}

Fetching particular data of current user from Firestore [Swift]

here you are a screenshot of a result and I would like to grab skills object of current user in firestore into the tableview. Any feedback regarding this?
func getSkills() {
guard (Auth.auth().currentUser?.uid) != nil else {
return
}
self.db.collection("tutors").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let docId = document.documentID
let addedSkills = document.get("skills") as! Array<Any>
print(docId, addedSkills)
}
}
}
}
As mentioned, it seems to be because you are declaring another addedSkills array within the completion block of your query. You should change it to this
func getSkills() {
guard (Auth.auth().currentUser?.uid) != nil else {
return
}
self.db.collection("tutors").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let docId = document.documentID
self.addedSkills = document.get("skills") as! Array<Any> // This is the change
print(docId, addedSkills)
}
}
}
}
The question seems way too generic, I think you can get away with any UITableView tutorial (with networking) out there. However, there are some points you may be aware regardless.
The addedSkills in you example above seems scoped local to the callback. You may remove the let before addedSkills, so compiler can pick up the right reference. Also don't forget to call tableView.reloadData once you have populated the view with new data.
In addition, you may spare force unwrapping things to avoid crashing you app, something like below.
if let documents = querySnapshot?.documents
{
for document in documents
{
if let addedSkills = document.get("skills") as? Array<Any>
{
// Log.
let documentID = document.documentID
print(documentID, addedSkills)
// Update data.
self.addedSkills = addedSkills
// Update UI.
self.tableView.reloadData()
}
}
}
You may want to be more conscious about selecting the right document though, instead of iterating over each. Try setting a breakpoint to your print statement to see the entire (!) documents object.

Unable to fetch data from existing document within subcollection (Swift & Firestore)

Messaging Structure:
messages > currentUserID (document) > partnerID (collection) > message (document)
I can get as far as retrieving the partner ID but I can't retrieve the individual messages (documents) within the collection. Heres the functions Im using:
func observeUserMessages(){
guard let uid = Auth.auth().currentUser?.uid else { return }
let dbRef = db.collection("messages").document(uid).addSnapshotListener { (querySnapshot, error) in
guard let snapshot = querySnapshot?.documentID else { return }
print("This is the partner ID: \(snapshot)")
self.fetchMessageWithPartnerID(partnerID: snapshot)
}
self.tableView.reloadData()
}
fileprivate func fetchMessageWithPartnerID(partnerID: String) {
guard let uid = Auth.auth().currentUser?.uid else { return }
Firestore.firestore().collection("messages").document(uid).collection(partnerID).getDocuments { (snapshot, err) in
print("This is the snapchat count:\(snapshot?.count)")
}
}
Results:
As you can see, it should show the two messages but its not returning anything.
I think there's a difference between .collection() and .document(). Try
Firestore.firestore().collection("messages").collection(uid).collection(partnerID).getDocuments { (snapshot, err) in
print("This is the snapchat count:\(snapshot?.count)")
}