I'm new to firebase and I wanted to try using a realtime database. It's been fairly easy to use until it's time to fetch the data from the database. I'm currently stuck at trying to get the data back into an array to provide my tableView with data.
Each image is saved with a timestamp. I've fetching it in different ways, it ends up printing nil or breaking the app. But when i use a breakpoint, I'm able to see the same data.
func downloadFromFirebase(completion: #escaping (Bool, Error?) -> Void) {
ref.child("ImageDetails/").observeSingleEvent(of: .value) { (snapshot) in
guard let value = snapshot.value as? [String:Any] else { return }
let name = value["username"] as! String
completion(true, nil)
}
}
I don't understand your question so I will write a code in order to retrieve the data from realtime database and to save it in an array.
You said "Each image is saved with a timestamp", so I assume you want to retrieve an array of data containing images(imageURL, etc) right?
we create a structure based on your database
struct dataStructure {
var Description:String?
var imageURL:String?
var Portfolio:String?
var createdAt:String?
var Instagram:String?
var profileImageUrl:String?
var Twitter:String?
var Username:String?
}
// now we create an array of this struct
var arrayOfData = [dataStructure]()
func downloadFromFirebase(completion:#escaping (Bool ,Error?)-> Void) {
ref.child("ImageDetails/").observeSingleEvent(of: .value) { snapshot in
guard let value = snapshot.value as? [[String:Any]] else { return }
// we add each element of value in the arrayOfData
for element in value {
guard let name = value["username"] as! String,
let Description = value["Description"] as! String,
let imageURL = value["imageURL"] as! String,
let Portfolio = value["Portfolio"] as! String,
let creationDate = value["createdAt"] as! String,
let Instagram = value["Instagram"] as! String,
let profileImageUrl = value["profileImageUrl"] as! String,
let Twitter = value["Twitter"] as! String ,
let Username = value["Username"] as! String else {
completion(false)
return
}
arrayOfData.append(dataStructure(name: name,
Description: Description,
imageURL: imageURL,
Portfolio: Portfolio ,
creationDate:creationDate ,
Instagram: Instagram,
profileImageUrl: profileImageUrl,
Twitter: Twitter))
}
completion(true)
}
}
at the end you will have an array with all your data
here is the code if you want to take the data from this function in your completion
func downloadFromFirebase(completion:#escaping (Result<dataStructure, Error>) {
ref.child("ImageDetails/").observeSingleEvent(of: .value) { snapshot in
guard let value = snapshot.value as? [[String:Any]] else { return }
// we add each element of value in the arrayOfData
for element in value {
guard let name = value["username"] as! String,
let Description = value["Description"] as! String,
let imageURL = value["imageURL"] as! String,
let Portfolio = value["Portfolio"] as! String,
let creationDate = value["createdAt"] as! String,
let Instagram = value["Instagram"] as! String,
let profileImageUrl = value["profileImageUrl"] as! String,
let Twitter = value["Twitter"] as! String ,
let Username = value["Username"] as! String else {
completion(.failure(error)
return
}
arrayOfData.append(dataStructure(name: name,
Description: Description,
imageURL: imageURL,
Portfolio: Portfolio ,
creationDate:creationDate ,
Instagram: Instagram,
profileImageUrl: profileImageUrl,
Twitter: Twitter))
}
/* we provide the array with the new data in the completion at the
end of the loop */
completion(.success(arrayOfData))
}
}
Related
I am trying to fetching conversation from firebase but it's showing failedTofetch data. It is fetching from firebase but in guard let statement it fails.
On successful i get a 2D array but on failure I get dictionary as output.
public func getAllMessageForConversation(with id:String,completion: #escaping (Result<[Message], Error>) -> Void){
print("folder for conversation \(id)/message")
database.child("\(id)/message").observe(.value, with: {snapshot in
guard let value = snapshot.value as? [[String:Any]] else{
completion(.failure(databaseError.failedToFetch))
print(snapshot.value!)
return
}
let messages: [Message] = value.compactMap({dictionary in
guard let name = dictionary["name"] as? String,
//let isread = dictionary["is_read"] as? Bool,
let messageId = dictionary["id"] as? String,
let content = dictionary["content"] as? String,
let senderEmail = dictionary["sender_email"] as? String,
let dateString = dictionary["date"] as? String,
//let type = dictionary["type"] as? String,
let date = ChatViewController.dataFormater.date(from: dateString)else{
return nil
}
let sender = Sender(photoUrl: " ", senderId: senderEmail, displayName: name)
return Message(sender: sender,
messageId: messageId,
sentDate: date,
kind: .text(content))
})
completion(.success(messages))
})
}
}
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 am currently learning Swift and I decided to make an iOS messaging app using Firebase. I am using JSQMessageViewController as my chat template and everything is working fine except for the fact that the app crashes when two users talking to each other are in the chat room at the same time. I am getting this error near the bottom of the function below: "Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)"
Here is my code for observing and retrieving message data. I call this everytime the view appears:
private func observeMessages() {
messageRef = ref.child("ChatRooms").child(chatRoomId!).child("Messages")
let messageQuery = messageRef.queryLimited(toLast:25)
newMessageRefHandle = messageQuery.observe(.childAdded, with: { (snapshot) in
let messageData = snapshot.value as! Dictionary<String, AnyObject>
if let data = snapshot.value as? [String: AnyObject],
let id = data["sender_id"] as? String,
let name = data["name"] as? String,
let text = data["text"] as? String,
let time = data["time"] as? TimeInterval,
!text.isEmpty
{
if id != uid! {
let updateRead = ref.child("ChatRooms").child(self.chatRoomId!).child("Messages").child(snapshot.key)
updateRead.updateChildValues(["status":"read"])
}
if let message = JSQMessage(senderId: id, senderDisplayName: name, date: Date(timeIntervalSince1970: time), text: text)
{
self.messages.append(message)
self.finishReceivingMessage()
}
}else if let id = messageData["senderId"] as! String!,
let photoURL = messageData["photoURL"] as! String! { // 1
if let mediaItem = JSQPhotoMediaItem(maskAsOutgoing: id == self.senderId) {
self.addPhotoMessage(withId: id, key: snapshot.key, mediaItem: mediaItem)
if photoURL.hasPrefix("gs://") {
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: nil)
}
}
}else {
print("Error! Could not decode message data")
}
})
updatedMessageRefHandle = messageRef.observe(.childChanged, with: { (snapshot) in
let key = snapshot.key
//I am getting an error on this line
let messageData = snapshot.value as! Dictionary<String, String>
if let photoURL = messageData["photoURL"] as String! {
// The photo has been updated.
if let mediaItem = self.photoMessageMap[key] {
self.fetchImageDataAtURL(photoURL, forMediaItem: mediaItem, clearsPhotoMessageMapOnSuccessForKey: key)
}
}
})
}
Curious to what I might be doing wrong here. All help is appreciated!
I have an app where users can like posts and I want to determine if the current user has previously liked a post in an efficient manner. My data currently looks like this:
I also store the likes for every user
In my current query I am doing this:
if let people = post["peopleWhoLike"] as? [String: AnyObject] {
if people[(Auth.auth().currentUser?.uid)!] != nil {
posst.userLiked = true
}
}
However, I believe this requires me to download all of the post's likes which isn't very efficient, so I tried this:
if (post["peopleWhoLike\(Auth.auth().currentUser!.uid)"] as? [String: AnyObject]) != nil {
posst.userLiked = true
}
The second method doesn't seem to be working correctly. Is there a better way to do this?
Here is my initial query as well:
pagingReference.child("posts").queryLimited(toLast: 5).observeSingleEvent(of: .value, with: { snap in
for child in snap.children {
let child = child as? DataSnapshot
if let post = child?.value as? [String: AnyObject] {
let posst = Post()
if let author = post["author"] as? String, 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 table = post["group"] as? String, let userID = post["userID"] as? String, let numberOfComments = post["numberOfComments"] as? Int, let region = post["region"] as? String, let numLikes = post["likes"] as? Int {
Solved it:
In the tableView I just query for the liked value directly and then determine what button to display.
static func userLiked(postID: String, cell: BetterPostCell, indexPath: IndexPath) {
// need to cache these results so we don't query more than once
if newsfeedPosts[indexPath.row].userLiked == false {
if let uid = Auth.auth().currentUser?.uid {
likeRef.child("userActivity").child(uid).child("likes").child(postID).queryOrderedByKey().observeSingleEvent(of: .value, with: { snap in
if snap.exists() {
newsfeedPosts[indexPath.row].userLiked = true
cell.helpfulButton.isHidden = true
cell.notHelpfulButton.isHidden = false
}
})
likeRef.removeAllObservers()
}
}
}
Called in my TableView:
DatabaseFunctions.userLiked(postID: newsfeedPosts[indexPath.row].postID, cell: cell, indexPath: indexPath)
I'm trying to fill the collectionView with posts. I have to get the posts, then get some data for the users who posted them. For some reason it isn't working.
DataService.ds.REF_POSTS.child("\(self.loggedInUser!.uid)").queryLimitedToLast(30).observeSingleEventOfType(.Value, withBlock: { postDictionary in
if postDictionary.exists() {
if let snapshots = postDictionary.children.allObjects as? [FIRDataSnapshot] {
self.posts = [Post]()
for snap in snapshots {
if let postDict = snap.value as? NSDictionary {
for(name, value) in postDict {
let interval = postDict.objectForKey("timePosted") as! Double
let formattedDate = NSDate(timeIntervalSince1970: interval)
let timeAgo = self.getDate(formattedDate)
if name as! String == "postedBy" {
DataService.ds.REF_USERS.child(value as! String).observeSingleEventOfType(.Value, withBlock: { (userDictionary) in
let userDict = userDictionary.value as! NSDictionary
let username = userDict.objectForKey("username")!
let profileThumbUrl = userDict.objectForKey("profileThumbUrl")!
let key = snap.key
let post = Post(postKey: key, dictionary: postDict, username: username as! String, profileThumbUrl: profileThumbUrl as! String, timeAgo: timeAgo)
self.posts.append(post)
})
}
}
}
}
}
}
self.collectionView?.reloadData()
})
It works if I perform the reload() right after appending the posts, but there is some sort of memory leak. There isn't a problem in the Post class or filling the collection view, if I use dummy values. The problem is in this code that I posted. I think I have an extra loop or something can anyone help?
If you fear that reload() is the reason of the memory leak , you can use this hack:-
if name as! String == "postedBy" {
DataService.ds.REF_USERS.child(value as! String).observeSingleEventOfType(.Value, withBlock: { (userDictionary) in
let userDict = userDictionary.value as! NSDictionary
let username = userDict.objectForKey("username")!
let profileThumbUrl = userDict.objectForKey("profileThumbUrl")!
let key = snap.key
let post = Post(postKey: key, dictionary: postDict, username: username as! String, profileThumbUrl: profileThumbUrl as! String, timeAgo: timeAgo)
self.posts.append(post)
if posts.count == postDictionary.childrenCount{
self.collectionView?.reloadData()
}
})
}
Then also see this answer :- Firebase observeSingleEventOfType stays in memory