How to retrieve the keys of a comment/post - ios

I'm having a hard time trying to retrieve both the postId and commentId at the same time.
The goal is to allow a user to delete their own comment on a post.
Here is the Comment Struct, below is a the function that should delete the users comment, however for some reason I can only return the commentId and not the postId. So the comment is not deleted when clicked.
The postId will = nil if I run a break point in the final like of deleteComment, but if I run a break point on the final line of func fetchComment the postId will return whatever the postId is for the post.
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 }
guard let commentId = self.commentId else { return }
let commentsRef = Database.database().reference().child("comments")
commentsRef.child(postId).child(commentId).removeValue()
}
Here is how comments are fetched
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
//print(commentId)
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")
}
}
Also, here is the code when users submit a comment
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()
}
}
Json for comments via firebase
"comments" : {
"-Lord0UWPkh5YdGIHtAO" : {
"-Lp7AQzHccme5RcRsyDd" : {
"creationDate" : 1.568874020882821E9,
"text" : "Wow Cool!",
"uid" : "wELwYnMGxxW0LJNRKBuuE2BUaV93"
}
},
"-LowvCk-agvJbK0VF-Bq" : {
"-LpKm1NcgOsXhwj6soxE" : {
"creationDate" : 1.569102243436777E9,
"text" : "Nice!",
"uid" : "wELwYnMGxxW0LJNRKBuuE2BUaV93"
},

One option is to make postId a property in the Comment struct and add it to the init method, this way you will always has access to it
struct Comment {
let postId: String
var commentId: String!
let user: User
var creationDate: Date!
let text: String
let uid: String!
init(commentId: String!,user: User, postIdentifier: String, dictionary: [String: Any]) {
self.commentId = commentId
self.user = user
self.postId = postIdentifier
...

Related

What is the solution to the error "The value of type [User] 'cannot be converted to the expected argument type' User"?

In my page named service, xcode points to the user and gives an error. but it doesn't work. What do you think should I change?
my user is already optional. I think it is an index problem but I don't know how to solve it I would appreciate it if you could help.where do you think the problem
message.swift
import Firebase
struct Message {
let text: String
let toId: String
let fromId: String
var timestamp: Timestamp!
var user: User?
let isFromCurrentUser :Bool
init(dictionary: [String: Any]) {
self.text = dictionary["text"] as? String ?? ""
self.toId = dictionary["toId"] as? String ?? ""
self.fromId = dictionary["fromId"] as? String ?? ""
self.timestamp = dictionary["timestamp"] as? Timestamp ?? Timestamp(date: Date())
self.isFromCurrentUser = fromId == Auth.auth().currentUser?.uid
}
}
struct Conversation {
let user: User
let message : Message
}
Service.Swift
import Firebase
struct Service {
static func fetchUsers (completion: #escaping([User]) -> Void) {
var users = [User] ()
COLLECTION_USERS.getDocuments { (snapshot, error) in
snapshot?.documents.forEach({ (document) in
let dictionary = document.data()
let user = User(dictionary: dictionary)
users.append(user)
completion(users)
})
}
}
static func fetchUser(widhtUid uid: String, completion:#escaping([User]) ->Void) {
COLLECTION_USERS.document(uid).getDocument { (snapshot, error) in
guard let dictionary = snapshot?.data() else {return}
let user = User(dictionary: dictionary)
completion(user)
}
}
static func fetchConversations (completion: #escaping([Conversation]) ->Void) {
var conversations = [Conversation]()
guard let uid = Auth.auth().currentUser?.uid else {return}
let query = COLLECTION_MESSAGES.document(uid).collection("recent-messages").order(by: "timestamp")
query.addSnapshotListener { (snapshot, error) in
snapshot?.documentChanges.forEach({ change in
let dictionary = change.document.data()
let message = Message(dictionary: dictionary)
self.fetchUser(widhtUid: message.toId) { user in
let conversation = Conversation(user:user, message: message)
conversations.append(conversation)
completion(conversations)
}
})
}
}
static func fetchMessages (forUser user: User, completion: #escaping([Message])-> Void) {
var messages = [Message]()
guard let currentUid = Auth.auth().currentUser?.uid else {return}
let query = COLLECTION_MESSAGES.document(currentUid).collection(user.uid).order(by: "timestamp")
query.addSnapshotListener{(snapshot,error) in
snapshot?.documentChanges.forEach({ change in
if change.type == .added {
let dictionary = change.document.data ()
messages.append(Message(dictionary: dictionary))
completion(messages)
}
})
}
}
static func uploadMessage(message: String, to user: User, completion: ((Error?)->Void)?) {
guard let currentUid = Auth.auth().currentUser?.uid else {return}
let data = ["text": message,
"fromId": currentUid,
"toId": user.uid,
"timestamp" : Timestamp(date: Date())] as [String : Any]
COLLECTION_MESSAGES.document(currentUid).collection(user.uid).addDocument(data:data) { _ in
COLLECTION_MESSAGES.document(user.uid).collection(currentUid).addDocument(data:data,completion:completion)
COLLECTION_MESSAGES.document(currentUid).collection("recent- messages").document(user.uid).setData(data)
COLLECTION_MESSAGES.document(user.uid).collection("recent- messages").document(currentUid).setData(data)
}
}
}
In this method:
static func fetchUser(widhtUid uid: String, completion:#escaping ([User]) -> Void)
The completion closure's parameter should be a User, not an array of users - [User].
Xcode should point you to the line where this error happens...
Anyway, here
static func fetchUser(widhtUid uid: String, completion:#escaping([User]) ->Void) {
COLLECTION_USERS.document(uid).getDocument { (snapshot, error) in
guard let dictionary = snapshot?.data() else {return}
let user = User(dictionary: dictionary)
completion(user)
}
}
Your completion:#escaping([User]) ->Void) expects an array [User] , but you invoke it with just one User object here completion(user)

Issues retrieving notificationTypes from Firebase

I have a bit of a lengthy question, So I apologize in advance I will try to illustrate this to the best of my abilities. I am trying to establish a notifications view controller that calls different types of data from Firebase and sets different notification types.
In the image above, this is how the cells should look when a user sends a notification to firebase. The user associated with that specific notification type as called and posted onto the screen.
In the firebase structure, We see that all of the information Stored is saved under the UID of the user in the first picture and is set under that specific users notification to show who is sending them a notification which is correct. These users names and images show perfectly as well as the image on the right.
The code I use to save this information is below,
fileprivate func saveSwipeToDataBase(didLike: Any) {
let swipeDate = Int(NSDate().timeIntervalSince1970)
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let cardUID = topCardView?.cardViewModel.uid else { return }
let documentData = ["workerId": uid,
"didLike": didLike,
"checked": 0,
"Swipe Date": swipeDate,
"type": SWIPE_INT_VALUE,
"posterId" : cardUID] as [String : Any]
self.postJobNotificationsIntoDatabseWithUID(uid: cardUID, values: documentData as [String : AnyObject])
}
private func postJobNotificationsIntoDatabseWithUID(uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference(fromURL: "https://oddjobs-b131f.firebaseio.com/")
let usersReference = ref.child("notifications").child(uid).childByAutoId()
usersReference.setValue(values, withCompletionBlock: { (err, ref) in
if err != nil {
print("error saving data into firebase")
return
}
})
}
And below is how I retrieve this information and store it onto the Notifications View controller.
func fetchNotifications() {
guard let currentUID = Auth.auth().currentUser?.uid else { return }
NOTIFICATIONS_REF.child(currentUID).observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }
print(dictionary)
for (_, postingRawData) in dictionary {
guard let postingDictionary = postingRawData as? Dictionary<String, AnyObject> else { continue }
guard let uid = postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["posterId"] as? String {
Database.fetchPoster(with: postId, completion: {(poster) in
let notification = userNotifications(user: user, poster: poster, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
})
} else {
let notification = userNotifications(user: user, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
}
})
}
}
}
Now that I got the correct way to setup up and show out of the way, I will show my enum and how I am distinguishing the different types of calls from firebase.
class userNotifications {
// MARK: - establish notificationTypes
enum NotificationType: Int, Printable {
case swipe
case accepted
case confirmed
case completed
case pay
var description: String {
switch self {
case .swipe: return " swiped on your Job "
case .accepted: return " accepted you to complete the job, "
case .confirmed: return " confirmed the job"
case .completed: return " completed the job"
case .pay: return " pay for completed"
}
}
init(index: Int) {
switch index {
case 0: self = .swipe
case 1: self = .accepted
case 2: self = .confirmed
case 3: self = .completed
case 4: self = .pay
default: self = .swipe
}
}
}
// MARK: - access firebaseData
var creationDate: Date!
var timeDate: Date!
var uid: String!
var fromId: String?
var workerId: String?
var user: User!
var poster: Poster!
var type: Int?
var notificationType: NotificationType!
var didCheck = false
init(user: User? = nil, poster: Poster? = nil, dictionary: Dictionary<String, AnyObject>) {
self.user = user
if let poster = poster {
self.poster = poster
}
if let swipeDate = dictionary["Swipe Date"] as? Double {
self.creationDate = Date(timeIntervalSince1970: swipeDate)
}
if let createDate = dictionary["creationDate"] as? Double {
self.creationDate = Date(timeIntervalSince1970: createDate)
}
if let swipeDate = dictionary["time&date"] as? Double {
self.timeDate = Date(timeIntervalSince1970: swipeDate)
}
if let type = dictionary["type"] as? Int {
self.notificationType = NotificationType(index: type)
}
if let uid = dictionary["uid"] as? String {
self.uid = uid
}
if let fromId = dictionary["fromId"] as? String {
self.fromId = fromId
}
if let workerId = dictionary["workerUID"] as? String {
self.workerId = workerId
}
if let checked = dictionary["checked"] as? Int {
if checked == 0 {
self.didCheck = false
} else {
self.didCheck = true
}
}
}
}
Above is the different types of notifications to be set.
Now, My issue is If I call a different notification type, such as .accepted, the information calls in a very different way.
The image above seems correct, However, the name and image are incorrect. it should be from the user ZacheryWilcox instead of Cjbwjdhbe. the user Cjbwjdhbe is the current user and the user who should be receing a notification from Zacherywilcox. not from itself.
In firebase, the information is saved as
the code I use to save this information is below
var workerUser: User? {
didSet {
let name = workerUser?.name
workerNameLabel.text = name
let workersUID = workerUser?.uid
workerNameLabel.text = name
guard let profileImage = workerUser?.profileImageUrl else { return }
workerImageView.loadImageUsingCacheWithUrlString(profileImage)
}
}
func saveUserData() {
let workUser = self.workerUser
guard let uid = Auth.auth().currentUser?.uid else { return }
let workerId = workUser?.uid
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value, with: { (snapshot) in
guard let dictionary = snapshot.value as? [String : Any] else { return }
let user = User(dictionary: dictionary as [String : AnyObject])
workUser?.uid = snapshot.key
self.datePicker.datePickerMode = UIDatePicker.Mode.date
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMMM dd yyyy/ hh:mm a"
let selectedDate = dateFormatter.string(from: self.datePicker.date)
let creationDate = Int(NSDate().timeIntervalSince1970)
print(selectedDate)
let docData: [String: Any] = [
"workerId": workerId!,
"time&date": selectedDate,
"posterId" : uid,
"creationDate": creationDate,
"location": user.address!,
"type": 1,
"jobPost": "someUIDString",
"checked": 0,
]
self.postJobNotificationsIntoDatabseWithUID(uid: workerId!, values: docData as [String : AnyObject])
}, withCancel: { (err) in
print("attempting to load information")
})
print("Finished saving user info")
self.dismiss(animated: true, completion: {
print("Dismissal complete")
})
}
private func postJobNotificationsIntoDatabseWithUID(uid: String, values: [String: AnyObject]) {
let ref = Database.database().reference(fromURL: "https://oddjobs-b131f.firebaseio.com/")
let usersReference = ref.child("notifications").child(uid).childByAutoId()
usersReference.setValue(values, withCompletionBlock: { (err, ref) in
if err != nil {
print("error saving data into firebase")
return
}
})
}
When the type .accepted is being used to differentiate what notificationType is being called, the user who sent the notification is not being set correctly and I have no idea what is the reasoning behind this. The correct user that is sending this information over is Zacherywilcox, and that users image and name should be set to the user's notification screen. not the user Cjbe... I was wondering if anyone could help me fix these issues. Thank you in advance. I'm starting to think that the way I am saving the users information when accepting the user is incorrect.
When I am fetchingNotifications(), is it possible that since calling
guard let uid = postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["posterId"] as? String {
has an effect on whats going on? if so, Is there a way to differentiate between what notificationType is being called and fetch what notifications has been called with their respective users?
Just update your code to:
func fetchNotifications() {
guard let currentUID = Auth.auth().currentUser?.uid else { return }
NOTIFICATIONS_REF.child(currentUID).observeSingleEvent(of: .value) { (snapshot) in
guard let dictionary = snapshot.value as? Dictionary<String, AnyObject> else { return }
print(dictionary)
let notificationId = snapshot.key
for (_, postingRawData) in dictionary {
guard let postingDictionary = postingRawData as? Dictionary<String, AnyObject> else { continue }
guard let type = postingDictionary["type"] as? Int else { continue }
guard let uid = (type == userNotifications.NotificationType.accepted.rawValue) ? postingDictionary["fromId"] as? String : postingDictionary["workerId"] as? String else { continue }
Database.fetchUser(with: uid, completion: { (user) in
if let postId = postingDictionary["fromId"] as? String {
Database.fetchPoster(with: postId, completion: {(poster) in
let notification = userNotifications(user: user, poster: poster, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
})
} else {
let notification = userNotifications(user: user, dictionary: postingDictionary)
self.notifications.append(notification)
self.handleSortNotification()
}
// NOTIFICATIONS_REF.child(currentUID).child(notificationId).child("checked").setValue(1)
})
}
}
}
This will solve your problem.

How to retrieve autoId to delete value

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

How to access a Particular Value from a struct

I created a struct UserModel.swift to store the json data:-
struct UserModel {
var id: Int = 0
var uuid:Int = 0
var user_name: String = ""
var password: String = ""
var name: String = ""
var email: String = ""
init(json: [String:Any]) {
if let obj = json["id"] as? Int {
self.id = obj
}
if let obj = json["uuid"] as? Int {
self.uuid = obj
}
if let obj = json["user_name"] as? String {
self.user_name = obj
}
if let obj = json["name"] as? String {
self.name = obj
}
if let obj = json["email"] as? String {
self.email = obj
}
}
}
Now I used Alamofire to get the json in my ViewController.Swift file and I stored my struct by creating a variable I am successfully get the json and stored it in my struct.
var userModel = [UserModel]()
private func getList() {
progressHUD.show(in: view, animated: true)
// let uuid = UserDefaults.standard.integer(forKey: "uuid")
Alamofire.request(Constants.API.url("list_request?device_token=\(device_token ?? "")&uuid=794849"), method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON {
(response:DataResponse<Any>) in
self.progressHUD.dismiss()
guard let json = response.result.value as? [String:Any] else {return}
guard let data = json["data"] as? [[String:Any]] else {return}
printD(data)
guard let status = json["status"] as? Bool else { return}
printD(status)
if status == true {
guard let data = json["data"] as? [[String:Any]] else {return}
for userData in data {
self.userModel.append(UserModel(json: userData))
printD(self.userModel)
}
CommonClass.shared.showSuccessMessage("\(json["msg"] as? String ?? "")", inViewController: self)
}
else if status == false {
CommonClass.shared.showErrorMessage("\(json["msg"] as? String ?? "")", inViewController: self)
}
else {
CommonClass.shared.showErrorMessage("Server Connection Error. Please try again later", inViewController: self)
}
self.tableView.reloadData()
}
}
Now the problem I am getting I have to use my userModel data in another api in same class and I don't know How Can I do that. In rejectRequest() function I need to access some of my struct data but I don't know how can I do that. Please help?
private func rejectRequest() {
let user = userModel // I need to use userModel data for parameter
let param: [String:Any] = ["from_user": "", "to_user": "", "request_id": "", "device_token": device_token ?? ""]
Alamofire.request(Constants.API.url("end_request"), method: .post, parameters: param, encoding: JSONEncoding.default, headers: nil).responseJSON {
(response:DataResponse<Any>) in
}
}
Create a struct to hold common data that is shared across the app. Here's a sample for a DataController that can hold different models or arrays / dicts of models:
struct DataController {
// MARK: - User management
struct Users {
static var users: [User] = []
static var currentUser: User = User()
static func addUser(_ user: User) {
users.append(user)
}
static func findUserById(_ id: Int) -> User? {
// Find the users that match the id (should only be one!)
let users = user.filter { $0.id = id }
return users.count == 1 ? users[0] : nil
}
}
}
Add a user:
DataController.Users.addUser(user)
Access from elsewhere by:
DataController.Users.currentUser
Find a user by an id:
if let user = DataController.Users.findUserById(id) {
... do stuff ...
}
Incidentally, might be simpler to use a guard statement when parsing your JSON data:
init(json: [String:Any]) {
guard
let id = json["id"] as? Int,
let uuid = json["uuid"] as? Int,
let user_name = json["user_name"] as? String,
let name = json["name"] as? String,
let email = json["email"] as? String
else {
return // Fail
}
self.id = id
self.uuid = uuid
self.user_name = user_name
self.name = name
self.email = email
}

Crash when trying to use .reversed() to sort Firebase fetch

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.

Resources