failedToFetch error in swift (IOS) firebase - ios

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))
})
}
}

Related

Accessing data in firebase realtime database swift

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))
}
}

manually entered data in firebase issue

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

App Crashes When Observing Real Time Chat Data with Firebase-iOS

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!

Let constant issue

I have this function that is grabbing events from firebase that a specific user is attending. Everything seems to be working up until the point where I try to alter an element relative to the event snapshot.
I get the error
Cannot assign to property: 'event' is a 'let' constant
//will show the vents that a user is attending
static func Events(for user: User, completion: #escaping ([Event]) -> Void)
{
var currentEvents = [Event]()
//Getting firebase root directory
let ref = Database.database().reference().child("users").child(user.uid).child("Attending")
ref.observe(.value, with: { (snapshot) in
// print(snapshot)
// guard snapshot.children.allObjects is [DataSnapshot] else {
// return completion([])
// }
guard let eventDictionary = snapshot.value as? [String: Any] else {
return completion([])
}
// print(snapshot)
let dispatchGroup = DispatchGroup()
eventDictionary.forEach({ (key,value) in
// print(key)
// print(value)
EventService.show(forEventKey: key , completion: { (event) in
dispatchGroup.enter()
AttendService.isEventAttended(event, byCurrentUserWithCompletion: { (isAttended) in
//error happens here
event?.isAttending = isAttended
dispatchGroup.leave()
})
currentEvents.append(.init(currentEventKey: key , dictionary: (event?.eventDictionary)!))
completion(currentEvents)
})
})
})
}
It is really confusing to me because I can't see where I declared it as a let constant.
This is the code for the method I am using to gather event info
static func show(forEventKey eventKey: String, completion: #escaping (Event?) -> Void) {
// print(eventKey)
let ref = Database.database().reference().child("events").child(eventKey)
// print(eventKey)
//pull everything
ref.observeSingleEvent(of: .value, andPreviousSiblingKeyWith: { (snapshot,eventKey) in
// print(snapshot.value ?? "")
guard let event = Event(snapshot: snapshot) else {
return completion(nil)
}
completion(event)
})
}
Below is my model for an event object
import Foundation
import FirebaseDatabase.FIRDataSnapshot
struct Event: Keyed {
var key: String?
let currentEventName: String
let currentEventImage: String
let currentEventPromo: String?
let currentEventDescription: String
//nested properties
let currentEventStreetAddress: String
let currentEventCity: String
let currentEventState: String
let currentEventDate: String?
let currentEventTime: String?
let currentEventEndTime: String?
let currentEventZip: Int
var category: String
//nested properties stop
var currentAttendCount: Int
var isAttending = false
var eventDictionary: [String: Any]{
let dateDict = ["start:date":currentEventDate, "start:time": currentEventTime,"end:time":currentEventEndTime]
return ["event:name":currentEventName,"event:imageURL" : currentEventImage,
"event:description": currentEventDescription, "attend:count": currentAttendCount,
"event:street:address": currentEventStreetAddress,"event:zip": currentEventZip,
"event:state": currentEventState, "event:city": currentEventCity, "event:promo": currentEventPromo ?? "", "event:date": dateDict, "event:category":category]
}
init(currentEventKey: String, dictionary: [String:Any]) {
self.key = currentEventKey
self.currentEventName = dictionary["event:name"] as? String ?? ""
self.currentEventImage = dictionary["event:imageURL"] as? String ?? ""
self.currentEventDescription = dictionary["event:description"] as? String ?? ""
self.currentEventPromo = dictionary["event:promo"] as? String ?? ""
self.currentAttendCount = dictionary["attend:count"] as? Int ?? 0
self.category = dictionary["event:category"] as? String ?? ""
//nested properties
self.currentEventStreetAddress = dictionary["event:street:address"] as? String ?? ""
self.currentEventCity = dictionary["event:city"] as? String ?? ""
self.currentEventState = dictionary["event:state"] as? String ?? ""
self.currentEventZip = dictionary["event:zip"] as? Int ?? 0
let eventDate = dictionary["event:date"] as? [String: Any]
self.currentEventDate = eventDate?["start:date"] as? String ?? ""
self.currentEventTime = eventDate?["start:time"] as? String ?? ""
self.currentEventEndTime = eventDate?["end:time"] as? String ?? ""
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String : Any],
let currentEventName = dict["event:name"] as? String,
let currentEventImage = dict["event:imageURL"] as? String,
let currentEventDescription = dict["event:description"] as? String,
let currentEventPromo = dict["event:promo"] as? String,
let category = dict["event:category"] as? String,
let currentEventStreetAddress = dict["event:street:address"] as? String,
let currentEventCity = dict["event:city"] as? String,
let currentEventState = dict["event:state"] as? String,
let currentEventZip = dict["event:zip"] as? Int,
let currentAttendCount = dict["attend:count"] as? Int,
let eventDate = dict["event:date"] as? [String: Any],
let currentEventDate = eventDate["start:date"] as? String,
let currentEventTime = eventDate["start:time"] as? String,
let currentEventEndTime = eventDate["end:time"] as? String
else { return nil }
self.key = snapshot.key
self.currentEventName = currentEventName
self.currentEventImage = currentEventImage
self.currentEventDescription = currentEventDescription
self.currentEventStreetAddress = currentEventStreetAddress
self.currentEventCity = currentEventCity
self.currentEventState = currentEventState
self.currentEventZip = currentEventZip
self.currentAttendCount = currentAttendCount
self.currentEventPromo = currentEventPromo
self.currentEventDate = currentEventDate
self.currentEventTime = currentEventTime
self.currentEventEndTime = currentEventEndTime
self.category = category
}
}
Event is a struct, so its instances are passed by value. That is, the call completion(event) passes a copy of event to completion.
Since completion doesn't take the Event as an inout, it is treated as a let, not a var, so you cannot modify it.
You could change the type of completion to #escaping (inout Event?) -> Void. Then the Event will be treated as a var, and changes to it will be effectively copied back out to the caller.
However, that won't make a difference in your case, because you're creating a brand new Event instance for the call to completion (by saying guard let event = Event(snapshot: snapshot)). So even if you let completion modify the Event it receives, you just throw it away after. You also need to write it back to your database somehow after completion returns, if you want the change to isAttending to have any effect.

Conforming To Protocol/ Pagination

Im attempting to paginate my home feed of pictures as to not load all of my elements in one shot. I have an event model
import Foundation
import FirebaseDatabase.FIRDataSnapshot
struct Event: MGKeyed {
let currentEventKey: String
let currentEventName: String
let currentEventImage: String
let currentEventPromo: String?
let currentEventDescription: String
//nested properties
let currentEventStreetAddress: String
let currentEventCity: String
let currentEventState: String
let currentEventDate: String?
let currentEventTime: String?
let currentEventZip: Int
//nested properties stop
var currentAttendCount: Int
var isAttending = false
var eventDictionary: [String: Any]{
let dateDict = ["start:date":currentEventDate, "start:time": currentEventTime]
return ["event:name":currentEventName,"event:imageURL" : currentEventImage,
"event:description": currentEventDescription, "attend:count": currentAttendCount,
"event:street:address": currentEventStreetAddress,"event:zip": currentEventZip,
"event:state": currentEventState, "event:city": currentEventCity, "event:promo": currentEventPromo ?? "", "event:date": dateDict]
}
init(currentEventKey: String, dictionary: [String:Any]) {
self.currentEventKey = currentEventKey
self.currentEventName = dictionary["event:name"] as? String ?? ""
self.currentEventImage = dictionary["event:imageURL"] as? String ?? ""
self.currentEventDescription = dictionary["event:description"] as? String ?? ""
self.currentEventPromo = dictionary["event:promo"] as? String ?? ""
self.currentAttendCount = dictionary["attend:count"] as? Int ?? 0
//nested properties
self.currentEventStreetAddress = dictionary["event:street:address"] as? String ?? ""
self.currentEventCity = dictionary["event:city"] as? String ?? ""
self.currentEventState = dictionary["event:state"] as? String ?? ""
self.currentEventZip = dictionary["event:zip"] as? Int ?? 0
let eventDate = dictionary["event:date"] as? [String: Any]
self.currentEventDate = eventDate?["start:date"] as? String ?? ""
self.currentEventTime = eventDate?["start:time"] as? String ?? ""
}
init?(snapshot: DataSnapshot) {
guard let dict = snapshot.value as? [String : Any],
let currentEventName = dict["event:name"] as? String,
let currentEventImage = dict["event:imageURL"] as? String,
let currentEventDescription = dict["event:description"] as? String,
let currentEventPromo = dict["event:promo"] as? String,
let currentEventStreetAddress = dict["event:street:address"] as? String,
let currentEventCity = dict["event:city"] as? String,
let currentEventState = dict["event:state"] as? String,
let currentEventZip = dict["event:zip"] as? Int,
let currentAttendCount = dict["attend:count"] as? Int,
let eventDate = dict["event:date"] as? [String: Any],
let currentEventDate = eventDate["start:date"] as? String,
let currentEventTime = eventDate["start:time"] as? String
else { return nil }
self.currentEventKey = snapshot.key
self.currentEventName = currentEventName
self.currentEventImage = currentEventImage
self.currentEventDescription = currentEventDescription
self.currentEventStreetAddress = currentEventStreetAddress
self.currentEventCity = currentEventCity
self.currentEventState = currentEventState
self.currentEventZip = currentEventZip
self.currentAttendCount = currentAttendCount
self.currentEventPromo = currentEventPromo
self.currentEventDate = currentEventDate
self.currentEventTime = currentEventTime
}
}
This Event Wants Me to Conform To A Protocol of MGKeyed which is listed below
import Foundation
import UIKit
protocol MGKeyed {
var key: String? { get set }
}
class MGPaginationHelper <T: MGKeyed> {
let pageSize: UInt
let serviceMethod: (UInt, String?, #escaping (([T]) -> Void)) -> Void
var state: MGPaginationState = .initial
var lastObjectKey: String?
init(pageSize: UInt = 1, serviceMethod: #escaping (UInt, String?, #escaping (([T]) -> Void)) -> Void) {
self.pageSize = pageSize
self.serviceMethod = serviceMethod
}
// 1
func paginate(completion: #escaping ([T]) -> Void) {
// 2
switch state {
// 3
case .initial:
lastObjectKey = nil
fallthrough
// 4
case .ready:
state = .loading
serviceMethod(pageSize, lastObjectKey) { [unowned self] (objects: [T]) in
// 5
defer {
// 6
if let lastObjectKey = objects.last?.key{
self.lastObjectKey = lastObjectKey
}
// 7
self.state = objects.count < Int(self.pageSize) ? .end : .ready
}
// 8
guard let _ = self.lastObjectKey else {
return completion(objects)
}
// 9
let newObjects = Array(objects.dropFirst())
completion(newObjects)
}
// 10
case .loading, .end:
return
}
}
func reloadData(completion: #escaping ([T]) -> Void) {
state = .initial
paginate(completion: completion)
}
}
enum MGPaginationState {
case initial
case ready
case loading
case end
}
This method is responsible for the pagination. My first problem is that the pagination never stops and continues to load the same 1 or 2 elements I have made the assumption that it is because of the conforming of the protocol. How can I conform my event model to that protocol so it is getting a different key and so it knows when to stop?

Resources