Showing post data from logged in user only swift/xcode/firebase - swift

Hey I have been following tutorials online to try and make a social media app and it works fine where all the posts made by users populates in one scene but in another scene I would like for the users who are currently logged in to be able to see their posts only instead of having to find their posts from a bunch of posts made by other users, below are my code samples:
Posts Code:
import Foundation
import UIKit
import Firebase
class Posts{
var id: String
var author: UserProfile
var text: String
var timestamp:Double
var createdAt:Date
init(id:String, author:UserProfile,text:String,timestamp:Double) {
self.id = id
self.author = author
self.text = text
self.timestamp = timestamp
self.createdAt = Date(timeIntervalSince1970: timestamp / 1000)
}
static func parse(_ key:String, _ data:[String:Any]) -> Posts? {
if let author = data["author"] as? [String:Any],
let uid = author["uid"] as? String,
let name = author["name"] as? String,
let photoURL = author["photoURL"] as? String,
let url = URL(string:photoURL),
let text = data["text"] as? String,
let timestamp = data["timestamp"] as? Double {
let userProfile = UserProfile(uid: uid, name: name, photoURL: url)
return Posts(id: key, author: userProfile, text: text, timestamp:timestamp)
func performDeepQuery() {
guard let user = Auth.auth().currentUser else { return }
let uid = user.uid
let ref = Database.database().reference().child("posts")
let query = ref.queryOrdered(byChild: "author/uid").queryEqual(toValue: uid)
query.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
let text = postSnap.childSnapshot(forPath: "text").value as? String ?? "No Text"
print(text)
//same as above
}
})
}
}
return nil }
}
func numberOfSections(in tableView: UITableView) -> Int {
return 2
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
switch section {
case 0:
return theposts.count
case 1:
return fetchingMore ? 1 : 0
default:
return 0
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if indexPath.section == 0 { let cell = HomeViewControllerScene.dequeueReusableCell(withIdentifier: "postCell", for: indexPath) as! Posting_TableViewCell
cell.set(theposts: theposts[indexPath.row])
return cell}
else {
let cell = HomeViewControllerScene.dequeueReusableCell(withIdentifier: "loadingCell", for: indexPath) as! LoadingCell
cell.spinner.startAnimating()
return cell
}
}
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
cellHeights[indexPath] = cell.frame.size.height
}
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
return cellHeights[indexPath] ?? 72.0
}
Please someone help me out here I have been scratching my head for a while with this!!!

I believe the question is how to get the posts for a certain user. The structure looks good but no need to have a child node 'author' in each post so instead of this:
posts
post_id_0
author
author data
text: "Hello, World"
uid: "uid_0"
do this
posts
post_id_0
text: "Hello, World"
uid: "uid_0"
name: "usmaan"
photoURL:"https://firebasestorage..."
So now just query for this users posts (this is for Firestore, scroll down fo the RTDB solution)...
func getThisUsersPosts() {
let uid = "uid_0" //this users uid
self.db.collection("posts]").whereField("uid", isEqualTo: uid).getDocuments { (snapshot, error) in
if let err = error {
print(err.localizedDescription)
return
}
if let doc = snapshot?.documents {
for d in doc {
let text = d.get("text") as? String ?? "No Post Text"
print(text)
}
} else {
print("no posts found")
}
}
}
self.db points to my Firestore.
EDIT:
OP is using the Real Time Database so here's the code for that
func getThisUsersPosts() {
let uid = "uid_0"
let ref = self.ref.child("posts") //self.ref points to MY firebase.
let query = ref.queryOrdered(byChild: "uid").queryEqual(toValue: uid)
query.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
let text = postSnap.childSnapshot(forPath: "text").value as? String ?? "No Text"
print(text)
}
})
}
EDIT 2:
OP wants to keep their same structure.
To query for data that's two levels deep we use what's called Deep Query and will look something like this:
func performDeepQuery() {
let uid = "uid_0"
let ref = self.ref.child("posts")
let query = ref.queryOrdered(byChild: "author/uid").queryEqual(toValue: uid)
query.observeSingleEvent(of: .value, with: { snapshot in
let allPosts = snapshot.children.allObjects as! [DataSnapshot]
for postSnap in allPosts {
//populate your tableView datasource here
let post = PostClass()
post.postId = postSnap.key
post.name = postStap.childSnapshot("name").value as? String ?? "No Post Name"
post.text = postStap.childSnapshot("text").value as? String ?? "No Post Text"
self.postArray.append(post)
}
self.myTableView.reloadData()
})
}
which will perform a deep query on this structure
posts
post_0
author
uid: "uid_0"
name: "post 0 name"
text: "post 0 text"
The PostClass could be this
class PostClass {
var postId = ""
var name = ""
var text = ""
}
More Info:
To get the current users uid (which is covered in the getting started guide as well) and assuming you are authenticated (otherwise it will be nil)
guard let user = Auth.auth().currentUser else { return }
let uid = user.uid

Related

Sorting cells instead of table

My App checks which POI/ Feed/ Group the user is following. Based on that it loads comments with the content of the comment and the user name/picture. Those are loaded separately. My only issue is sorting by time.
If I use this code: self.table = self.table.sorted(by: { $0.userTime ?? 0 > $1.userTime ?? 0 }) the sorting of the comments is correct. But the matching of the profile names and pictures is totally wrong.
If I remove that code above the matching is correct but the sorting is totally wrong. How can I solve this? Instead of sorting the table I have to sort the cell itself?
func loadFollowedPoi() {
myFeed.myArray1 = []
let userID = Auth.auth().currentUser!.uid
let database = Database.database().reference()
database.child("user/\(userID)/abonniertePoi/").observeSingleEvent(of: .value, with: { snapshot in
for child in snapshot.children.allObjects as! [DataSnapshot] {
myFeed.myArray1.append(child.key)
}
self.postsLaden()
})
}
func postsLaden() {
dic = [:]
let neueArray: [String] = []
for groupId in myFeed.myArray1[0..<myFeed.myArray1.count] {
let placeIdFromSearch = ViewController.placeidUebertragen
ref = Database.database().reference().child("placeID/\(groupId)")
ref.observe(DataEventType.childAdded, with: { snapshot in
guard let dic = snapshot.value as? [String: Any] else { return }
let newPost = importPosts(dictionary: dic, key: snapshot.key)
guard let userUid = newPost.userID else { return }
self.fetchUser(uid: userUid, completed: {
self.table.insert(newPost, at: 0)
self.table = self.table.sorted(by: { $0.userTime ?? 0 > $1.userTime ?? 0 })
self.tableView.reloadData()
})
}
)}
}
func fetchUser(uid: String, completed: #escaping () -> Void) {
ref = Database.database().reference().child("user").child(uid).child("userInformation")
ref.observe(.value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else { return }
let newUser = UserModel(dictionary: dic)
self.users.insert(newUser, at: 0)
completed()
}
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.layoutMargins = UIEdgeInsets.zero
cell.post = table[indexPath.row]
cell.user = users[indexPath.row]
return cell
}
class importPosts {
var userID: String?
var userGroup: String?
var userComment: String?
var userTime: Int?
var userLikes: Int?
var commentId: String?
var placeID: String?
var kommentarCount: Int?
var id: String?
var likeCount: Int?
var likes: Dictionary<String, Any>?
var isLiked: Bool?
init(dictionary: [String: Any], key: String) {
userID = dictionary["userID"] as? String
userComment = dictionary["userComment"] as? String
userGroup = dictionary["userGroup"] as? String
userTime = dictionary["userTime"] as? Int
userLikes = dictionary["userLikes"] as? Int
commentId = dictionary["commentId"] as? String
placeID = dictionary["placeID"] as? String
kommentarCount = dictionary["kommentarCount"] as? Int
id = key
likeCount = dictionary["likeCount"] as? Int
likes = dictionary["likes"] as? Dictionary<String, Any>
ViewComments.commentIDNew = commentId!
if let currentUserUid = Auth.auth().currentUser?.uid {
if let likes = self.likes {
isLiked = likes[currentUserUid] != nil
}
}
}
}
As suggested in the comments create a parent struct which contains one user an one post respectively
struct UserData {
let user: UserModel
let post: importPosts
}
Side note: Please name structs/classes always uppercase and why not simply User and Post?
Create the datasource array
var users = [UserData]()
Modify fetchUser to pass the new user in the completion handler
func fetchUser(uid: String, completed: #escaping (UserModel) -> Void) {
ref = Database.database().reference().child("user").child(uid).child("userInformation")
ref.observe(.value) { (snapshot) in
guard let dic = snapshot.value as? [String: Any] else { return }
let newUser = UserModel(dictionary: dic)
completed(newUser)
}
}
And modify also postsLaden to assign the post and the associated user to the model
func postsLaden() {
//dic = [:]
//let neueArray: [String] = [] seems to be unused
for groupId in myFeed.myArray1[0..<myFeed.myArray1.count] {
let placeIdFromSearch = ViewController.placeidUebertragen
ref = Database.database().reference().child("placeID/\(groupId)")
ref.observe(DataEventType.childAdded, with: { snapshot in
guard let dic = snapshot.value as? [String: Any] else { return }
let newPost = importPosts(dictionary: dic, key: snapshot.key)
guard let userUid = newPost.userID else { return }
self.fetchUser(uid: userUid, completed: { user in
self.users.insert(UserData(user: user, post: newPost), at: 0)
self.users.sort($0.user.userTime ?? 0 > $1.user.userTime ?? 0)
self.tableView.reloadData()
})
}
)}
}
Finally modify cellForRow
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.layoutMargins = UIEdgeInsets.zero
let user = users[indexPath.row]
cell.post = user.post
cell.user = user.user
return cell
}
Yet another side note: Sorting and reloading the table view multiple times inside the loop is unnecessarily expensive. You could add DispatchGroup to sort and reload the data once on completion. Regarding expensive: In the database isn't it possible that Post can hold a full reference to the user to avoid the second fetch? For example Core Data can.

Can't load my current user's posts from Firebase - Swift

I want to load my current user's posts from my Firebase Database.
This is my database's architecture:
I have already successfully loaded all the posts into a home view but when I tried to duplicate and use the same method here with the necessary changes and using the uid for the current user's posts, it's not showing anything... Have run out of ideas as to what the problem might be, could use some help.
Here's a screenshot from the ProfileUserPosts swift file:
//VARS
var postsuser = [ProfileUserPosts]()
#objc func observeUserPosts() {
let uid = Auth.auth().currentUser?.uid
let postsRef = Database.database().reference().child("posts").child("author")
postsRef.queryOrdered(byChild: "userid").queryEqual(toValue: uid!).observe(.value) { (snapshot) in
var tempPost = [ProfileUserPosts]()
for child in snapshot.children {
if let childSnapshot = child as? DataSnapshot {
let dict = childSnapshot.value as? [String: Any]
//Post Picture
let photoUrl = dict!["photoUrl"] as? String
let url = URL(string: photoUrl!)
//Info Post
let comments = dict!["comments"] as? String
let city = dict!["city"] as? String
let municipality = dict!["municipality"] as? String
let breed = dict!["breed"] as? String
let phoneuser = dict!["phone"] as? String
let postType = dict!["postType"] as? String
let petType = dict!["petType"] as? String
let gender = dict!["gender"] as? String
let timestampadoption = dict!["timestamp"] as? Double
let date = Date(timeIntervalSince1970: timestampadoption!/1000)
let post = ProfileUserPosts(breed: breed!, phone: phoneuser!, photoUrl: url!, city: city!, municipality: municipality!, petType: petType!, gender: gender!, timestamp: date, postType: postType!, comments: comments!)
tempPost.insert(post, at: 0)
}
DispatchQueue.main.async {
self.postsuser = tempPost
self.postsCollectionView.reloadData()
self.refresher.endRefreshing()
}
}
}
}
Here's where I'm loading the CollectionView with the numberOfItemsInSection and cellForItem
extension ProfileViewController: UICollectionViewDataSource,UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return postsuser.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell: PostsCollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "postsCell", for: indexPath) as! PostsCollectionViewCell
cell.set(post: postsuser[indexPath.row])
return cell
}
}
Well, my guess - based on your database structure - is that you're accessing an invalid path.
When you've referenced Database.database().reference().child("posts").child("author"), you're saying that posts have a direct child to author and that's not true, they've a direct child of a post id, which then fetching the respective post, you can get the author, so in this case, you would've to iterate through all the posts.
So, to fetch all the posts:
let postsRef = Database.database().reference().child("posts")
then you would have an Object (very important - this is not an array) of posts with ID's as keys, then you would iterate through them and fetch the respective author you want. I won't get into details of your implementation, but just to clarify what I'm saying:
let specificPostAuthor = postsRef.child("LOj1UZZgUnKa28xVtht").child("author")
would return the respective post author.

Fetch data from multiple node in Firebase in Swift

I built my app to have news feed like Facebook. My problem is that I don't know how to fetch child images in Post and show it in a collectionView. Please show me how to do it. Appreciate any help.
Here is the db structure:
Posts
d7j3bWMluvZ6VH4tctQ7B63dU4u1:
20181112101928:
avatar: "https://platform-lookaside.fbsbx.com/platform/p..."
content: "Funny image"
images:
-LR4vaEIggkGekc-5ZME:
"https://firebasestorage.googleapis.com/v0/b/hon..."
-LR4vaENC-IsePibQYxY:
"https://firebasestorage.googleapis.com/v0/b/hon..."
name: "Thành Trung"
time: 1541992768776.3628
type: "Funny"
Here is my code:
func getDataFromPostFirebase() {
let getPostData = databaseReference.child("Posts")
getPostData.observe(.childAdded) { (snapshot) in
getPostData.child(snapshot.key).observe(.childAdded, with: { (snapshot1) in
getPostData.child(snapshot.key).child(snapshot1.key).observe(.value, with: { (snapshot2) in
self.arrayImageUrl = [String]()
if let dict = snapshot2.value as? [String : Any] {
guard let avatar = dict["avatar"] as? String else {return}
guard let content = dict["content"] as? String else {return}
guard let name = dict["name"] as? String else {return}
guard let time = dict["time"] as? Double else {return}
guard let type = dict["type"] as? String else {return}
if let images = dict["images"] as? [String : String] {
for image in images.values {
self.arrayImageUrl.append(image)
}
let newPost = Post(avatarString: avatar, contentString: content, nameString: name, timeDouble: time, typeString: type)
self.arrayPost.append(newPost)
DispatchQueue.main.async {
self.feedTableView.reloadData()
}
} else {
let newPost = Post(avatarString: avatar, contentString: content, nameString: name, timeDouble: time, typeString: type)
self.arrayPost.append(newPost)
DispatchQueue.main.async {
self.feedTableView.reloadData()
}
}
}
})
})
}
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayImageUrl.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as! TrangChu_CollectionViewCell
cell.imgContent.layer.cornerRadius = CGFloat(8)
cell.imgContent.clipsToBounds = true
cell.imgContent.layer.borderWidth = 2
cell.imgContent.layer.borderColor = #colorLiteral(red: 0.4756349325, green: 0.4756467342, blue: 0.4756404161, alpha: 1)
let url = URL(string: arrayImageUrl[indexPath.row])
cell.imgContent.sd_setImage(with: url, completed: nil)
return cell
}
Model object
import Foundation
class Post {
var avatar : String
var content : String
var images : [String]?
var name : String
var time : Double
var type : String
init(avatarString : String, contentString : String, nameString : String, timeDouble : Double, typeString : String) {
avatar = avatarString
content = contentString
// images = imagesString
name = nameString
time = timeDouble
type = typeString
}
}
As what I've said your db is not well structured. I suggest you re structure it like this.
Posts
d7j3bWMluvZ6VH4tctQ7B63dU4u1:
avatar: "https://platform-lookaside.fbsbx.com/platform/p..."
content: "Funny image"
images:
-LR4vaEIggkGekc-5ZME: "https://firebasestorage.googleapis.com/v0/b/hon..."
-LR4vaENC-IsePibQYxY: "https://firebasestorage.googleapis.com/v0/b/hon..."
name: "Thành Trung"
time: 1541992768776.3628
type: "Funny"
timestamp: 1540276959924
I removed the timestamp node and transferred it along the children node. Now you can fetch the posts with this.
ref.child("Posts").observe(.childAdded) { (snapshot) in
var post = Post()
let val = snapshot.value as! [String: Any]
post.name = val["name"] as? String
self.ref.child("Posts").child(snapshot.key).child("images").observeSingleEvent(of: .value, with: { (snap) in
post.imagesString = [String]()
for image in snap.children.allObjects as! [DataSnapshot] {
post.imagesString?.append(image.value as! String)
print("images \(image.value)")
}
list.append(post)
print("post \(post)")
})
If you want to order the posts you can achieve it using queryOrderedByChild("timestamp")
Add this to access your images:
guard let images = dict["images"] as? [[String: Any]] { return }
let imagesString: [String] = []
for imageDict in images {
for key in imageDict.keys {
if let imageName = imageDict[key] as? String else {
// here you access your image as you want
imagesString.append(imageName)
}
}
}
Then when creating the post object you use imagesString that we created:
let newPost = Post(avatarString: avatar, contentString: content, imagesString: imagesString, nameString: name, timeDouble: time, typeString: type)
You can fetch the images values using this
ref.child("Posts").observe(.childAdded) { (snapshot) in
self.ref.child("Posts").child(snapshot.key).observe(.childAdded, with: { (snapshot1) in
self.ref.child("Posts").child(snapshot.key).child(snapshot1.key).child("images").observe(.childAdded, with: { (snap) in
let post = new Post()
for rest in snap.children.allObjects as! [DataSnapshot] {
//append images
post.imagesString.append(rest.value)
}
post.avatarString = snapshot1.value["avatar"] as? String
...
})
})
I suggest you change the structure of your db because its nested. Refer here

UISearchBar and Firebase Database

struct postStruct {
let title : String!
let author : String!
let bookRefCode : String!
let imageDownloadString : String!
let status : String!
let reserved : String!
let category : String!
let dueDate : String!
}
'Above is where i set up the structure for the post, and below, is how i reference and retrieve the data from the firebase database.
My problem is that when you set up the searcher, i do not know how to get it to search based off of the title of the post.'
class DirectoryTableView: UITableViewController {
var posts = [postStruct]()
override func viewDidLoad() {
let databaseRef = Database.database().reference()
databaseRef.child("Books").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let title = snapshotValue!["title"] as? String
snapshotValue = snapshot.value as? NSDictionary
let author = snapshotValue!["author"] as? String
snapshotValue = snapshot.value as? NSDictionary
let bookRefCode = snapshotValue!["bookRefCode"] as? String
snapshotValue = snapshot.value as? NSDictionary
let status = snapshotValue!["status"] as? String
snapshotValue = snapshot.value as? NSDictionary
let reserved = snapshotValue!["reserved"] as? String
snapshotValue = snapshot.value as? NSDictionary
let category = snapshotValue!["category"] as? String
snapshotValue = snapshot.value as? NSDictionary
let dueDate = snapshotValue!["dueDate"] as? String
snapshotValue = snapshot.value as? NSDictionary
self.posts.insert(postStruct(title: title, author: author, bookRefCode: bookRefCode, status: status, reserved: reserved, category: category, dueDate: dueDate) , at: 0)
self.tableView.reloadData()
})
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let databaseRef = Database.database().reference()
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].author
let label3 = cell?.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].bookRefCode
let label4 = cell?.viewWithTag(4) as! UILabel
label4.text = posts[indexPath.row].status
let label5 = cell?.viewWithTag(5) as! UILabel
label5.text = posts[indexPath.row].category
let image1 = cell?.viewWithTag(6) as! UILabel
image1.text = posts[indexPath.row].imageDownloadString
let label6 = cell?.viewWithTag(7) as! UILabel
label6.text = posts[indexPath.row].reserved
let label9 = cell?.viewWithTag(9) as! UILabel
label9.text = posts[indexPath.row].dueDate
return cell!
}
'Also, does anyone know how to sort the tableview cells (posts in this case) alphabetically?'
You can get all data already ordered alphabetically
databaseRef.child("Books").queryOrdered(byChild: "title").observe(.childAdded, with: { snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let title = snapshotValue!["title"] as? String
snapshotValue = snapshot.value as? NSDictionary
....
}
or sort your array before reload the tableView
var sortedArray = swiftArray.sorted { $0.title.localizedCaseInsensitiveCompare($1.title) == ComparisonResult.orderedAscending }
Sample structure
for sorting data according to searchBar I had used an dictionary that having all my snapshot and I compared my searchBar text in that dict and after sorting reloaded tableView here is code that you can have a look at
//method to get all user Details in a dict
func getEmail() {
let databaseRef = Database.database().reference().child("users")
databaseRef.observe(.value, with: { (snapshot) in
if snapshot.exists(){
self.postData = snapshot.value as! [String : AnyObject]
let dictValues = [AnyObject](self.postData.values)
self.sarchDict = dictValues
}
})
}
//search bar delegate
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if self.mySearchBar.text!.isEmpty {
// set searching false
self.isSearching = false
}else{
// set searghing true
self.isSearching = true
self.names.removeAll()
self.uidArray.removeAll()
self.imageUrl.removeAll()
for key in self.sarchDict {
let mainKey = key
//I am making query against email in snapshot dict
let str = key["email"] as? String
//taking value of email from my dict lowerCased to make query as case insensitive
let lowercaseString = str?.lowercased()
//checking do my any email have entered letter or not
if(lowercaseString?.hasPrefix(self.mySearchBar.text!.lowercased()))!{
//here I have a check so to remove value of current logged user
if ((key["uID"] as! String) != (Auth.auth().currentUser?.uid)!){
//If value is found append it in some arrays
self.imageUrl.append( key["profilePic"] as! String )
self.names.append( key["name"] as! String )
self.uidArray.append( key["uID"] as! String )
//you can check which values are being added from which key
print(mainKey)
}
}
}
//reload TableView here
}
}
//TableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell = self.myTableView.dequeueReusableCell(withIdentifier: "Cell")!
if self.isSearching == true {
let imageView = (cell.viewWithTag(1) as! UIImageView)
imageView.setRounded()
if imageUrl[indexPath.row] != "" {
self.lazyImage.showWithSpinner(imageView:imageView, url:imageUrl[indexPath.row])
}
else{
imageView.image = UIImage(named: "anonymous")
}
(cell.contentView.viewWithTag(2) as! UILabel).text = self.names[indexPath.row]
}
else {
}
return cell
}
I'm sure this will be helpful to some using FireStore. Here I'm just setting my reference to point to the right collection. "name" is my field I wish to search by and is greater than will be checked chronologically on my string. The further they type the more defined the search results are.
static func searchForProgramStartingWith(string: String) {
let programsRef = db.collection("programs")
programsRef.whereField("name", isGreaterThan: string).limit(to: 10).getDocuments { (snapshot, error) in
if error != nil {
print("there was an error")
} else {
let shots = snapshot?.documents
for each in shots! {
let data = each.data()
let name = data["name"]
print("The name is \(name!)")
}
}
}
}

Default value for UITableView Cell if Firebase snapshot is nil

I have looked around and I cannot figure this out.
I am trying to set a default value of a tableView Cell if a Firebase snapshot returns nil
Example:
A snapshot is made to show all the event names from my Firebase Database
in a tableView using a dequeReusableCell.
But if the snapshot returns nil, the tableView returns with 1 cell with a label saying "Sorry, there are no events."
Here is my firebase snapshot code. This code does currently handle if the snapshot does return nil with a print() statement.
func populateTableView(){
let uid = Auth.auth().currentUser?.uid
ref = Database.database().reference()
ref.child("events").child(uid!).child(currentDate).observeSingleEvent(of: .value, with: { (snapshot) in
self.events = []
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshot {
//print("SNAP: \(snap)")
if let postDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let event = Event(postKey: key, postData: postDict)
self.events.append(event)
//print(self.events)
}
}
}
if !snapshot.exists() {
self.eventStatus = false
self.tableView.reloadData()
print("No Event here")
} else {
self.eventStatus = true
self.tableView.reloadData()
}
})
}
The firebase Objects get stored into the Event class and are stored a dictionary. I don't think this code is needed, but here is the event class code for more context.
import Foundation
import Firebase
class Event {
var ref: DatabaseReference!
private var _description: String!
private var _imageUrl: String!
private var _eventTitle: String!
private var _eventType: String!
private var _eventTime: String!
private var _eventStartDate: String!
private var _eventEndDate: String!
private var _monthlyRepeat: String!
private var _weeklyRepeat: String!
private var _eventColor: String!
private var _postKey: String!
private var _postRef: DatabaseReference!
var description: String {
return _description
}
var imageUrl: String {
return _imageUrl
}
var eventTitle: String {
return _eventTitle
}
var eventType: String {
return _eventType
}
var eventTime: String {
return _eventTime
}
var eventStartDate: String {
return _eventStartDate
}
var eventEndDate: String {
return _eventEndDate
}
var monthlyRepeat: String {
return _monthlyRepeat
}
var weeklyRepeat: String {
return _weeklyRepeat
}
var eventColor: String {
return _eventColor
}
var postKey: String {
return _postKey
}
init(postKey: String, postData: Dictionary<String, AnyObject>) {
self._postKey = postKey
if let description = postData["description"] as? String {
self._description = description
}
if let imageUrl = postData["event_Image_URL"] as? String {
self._imageUrl = imageUrl
}
if let eventTitle = postData["event_Title"] as? String {
self._eventTitle = eventTitle
}
if let eventType = postData["event_Type"] as? String {
self._eventType = eventType
}
if let eventTime = postData["event_Time"] as? String {
self._eventTime = eventTime
}
if let eventStartDate = postData["start_Date"] as? String {
self._eventStartDate = eventStartDate
}
if let eventEndDate = postData["end_Date"] as? String {
self._eventEndDate = eventEndDate
}
if let monthlyRepeat = postData["monthly_Repeat"] as? String {
self._monthlyRepeat = monthlyRepeat
}
if let weeklyRepeat = postData["weekly_Repeat"] as? String {
self._weeklyRepeat = weeklyRepeat
}
if let eventColor = postData["color"] as? String {
self._eventColor = eventColor
}
let uid = Auth.auth().currentUser?.uid
ref = Database.database().reference()
let eventRef = ref.child("events").child(uid!).child("Monday May, 29")
_postRef = eventRef.child(_postKey)
}
}
The simplest way to solve this is to add a title UILabel to your ViewcController and change the text when snapshot is not available.
Or if that doesn't work for you for some reason you could try this:
I did not check this, but I might get you on track.
First you will need to change your populateTableView method so that an events array is created even when snapshot has no results. This way the events array count will be 1 (and one row will be added to your tableView) even if snapshot had no result.
populateTableView(){
let uid = Auth.auth().currentUser?.uid
ref = Database.database().reference()
ref.child("events").child(uid!).child(currentDate).observeSingleEvent(of: .value, with: { (snapshot) in
self.events = []
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshot {
//print("SNAP: \(snap)")
if let postDict = snap.value as? Dictionary<String, AnyObject> {
let key = snap.key
let event = Event(postKey: key, postData: postDict)
self.events.append(event)
//print(self.events)
}
}
}
else{ // Snapshot does not exist
let postDict: Dictionary<String, AnyObject> // Add an empty Dictionary
let key = -1 // Or what ever value you could not possibly expect
let event = Event(postKey: key, postData: postDict)
self.events.append(event)
self.tableView.reloadData()
print("No Event here")
}
})
}
Notice that when snapshot is not valid or available you add an empty Dictionary with an unique key value to your events array.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count
}
You need to create two custom cells with unique identifiers.
Now you can "actually" populate your tableView similar to this:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let event = events.[indexPath.row]
let conditionKey = event.key
if(conditionKey == -1){ // or whatever value you gave in populateTableView to indicate that snapshot did not exist
let cell = tableView.dequeueReusableCell(withIdentifier: "identifierCellNotSoGood", for: indexPath) as! CustomCellNotSoGood
cell.noSnapShotLabel1.text = "Sorry, there are no events."
return cell
}
else{
let cell = tableView.dequeueReusableCell(withIdentifier: "identifierCellAllGood", for: indexPath) as! CustomCellAllGood
cell.yourCustomLabel1.text = event.key // Or whatever data you are displaying
cell.sourCustomLabel2.text = event.event // Or whatever data you are displaying
return cell
}
return UITableViewCell
}
If you need to handle the selection of a table cell you can do this:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// get rid of the ugly highlighting
tableView.deselectRow(at: indexPath, animated: false)
let event = events.[indexPath.row
let conditionKey = event.key
if(conditionKey == -1){ // or whatever value you gave in populateTableView to indicate that snapshot did not exist
// Do what you need or not
}
else{
// Do something meaningful with your database
doSomething(withEventData: event)
}
}