Firebase real time updates for posts from people I follow - ios

I'm trying to get real time updates working with Firestore on my app. However, I'm not sure if what I'm trying to do is possible in real time.
I have Users who follow other Users. When they post to the Posts collection, the users timeline should be updated in real time with the posts from users they follow. Is this possible?
I have an identifier userId on each post. Any help would be appreciated as I'm a bit stuck!

You can fetch list of users that a user follows and
let queryRef = self.ref.child("posts/\(userID)")
queryRef?.observe(.childAdded, with: { (snapshot) in
// new post from a friend
})

Suppose a user john is logged In. First of all get userIDs of all the persons whom john is following. Store the userIDs in an array.
Than use an observer to listen for changes in the posts table. Whenever value of posts table is changed,
check the userID of that post.
if the array contains this userID. update the news feed of the logged In User.
Hope that helps.

Related

Firebase delete query in iOS swift

I have list of comments in post.
On adding comments to post node , I have used childbyautoid().
and I have set commenttext, timestamp and userid as values for comment.
CASE:
I want to delete my comment in the post.
Problem:
How can I delete the comment according to userid = myuserid?
Went through many examples but couldn't found any?
You have to query all the node in comments. After querying all the autoId's in the node, you have to check for user id in each snapshot.
In that snapshot, you have to compare your user id and if it matches, you have take the autoId of that snapshot and delete it.

I'm trying to create a leaderboard using Firebase and show the username, highscore, region.

My goal is to create a leaderboard using Firebase and to show the username, high score and region in a tableview. My current Database looks like this
I have everything under user at the moment. I did it this way so i could increment data for the current user. Unfortunately, by doing it this way, I don't think I can let others see someone elses high score or username.
Is there a solution to this problem using the current node setup, or do I need to change it. And if so can someone give my examples of how the database should look.
You should separate your user specific data from the leaderboards.
You have your users node in which your user's data under their unique user id.
Then you also can create a leaderboards node and under the same unique user id, your leaderboard specific data. This way you won't have to fetch all the data at once only to know the user specific data.
The connection is the user id, that is the unique id, that your user is going to know, and retrieve his/her data by. The other players can just retrieve the leaderboards node by a query. You should also store some seemingly redundant data, for instance the username which you would both need for the user's node and for the leaderboards.
Don't forget to apply security rules, so the user can only write to his/her data.
Looking like this:
The authenticated user who knows his/her UID can then listen to his/her own leaderboard score by fetching data on this path: "leaderboards/UID" right.
Other players should just have to use the path: "leaderboards" to fetch every player on the leaderboard. To order them and only fetch the top 10 players you have to use queries.
Use queryOrderedByChild to order your data by highscore
For this you should also add the security rule: ".indexOn": "highscore".
To fetch the top10 players use
queryLimitedToFirst setting it to 10, it will only download the top 10 players.
You should read more about queries: https://firebase.google.com/docs/database/ios/lists-of-data#sorting_and_filtering_data
While using JSON the flatter the tree the better it is to
HighScore :{
userID1 : 100,
userID2 : 103;
userID3 : 210;
},
Users :{
userID1 :{...}
userID2 :{...}
userID3 :{...}
}
If you want to add a different relational property to the user, just add another parent node for that particular user and query it.
FIRDatabase.database().reference().child("HighScores").observeSingleEvent(of: .value, with: {(Snap) in
print(Snap.value)
})
PS:- You can always play with security rules in firebase , to manipulate the permissions for specific node in the Database to your authenticated users Docs Link.
To append your data in firebase node:-
FIRDatabase.database().reference().child("HighScores").observeSingleEvent(of: .value, with: {(Snap) in
if let snapDict = Snap.value as? NSMutableDictionary{
snapDict.setObject(_yourHighScore, forKey : FIRAuth.auth()!.currentUser!.uid as NSCopying)
FIRDatabase.database().reference().child("HighScores").setValue(snapDict)
}
})
You can store everything under user node if you want. And fetch user details by applying sort based on high score. You should also apply limit.
If you are planning to create separate node for user details and score then you must fetch score from the score node and based on user id fetch user detail from user node.
Adding on to #Durian 's answer, the code should look something like this,
let leaderboardDB = Database.database().reference().child("leaderboards")
leaderboardDB.queryOrdered(byChild: "score").queryLimited(toFirst: 100).observeSingleEvent(of: DataEventType.value, with: { (snapshot) in
if snapshot.exists() {
print("Retrieving Data!")
for child in snapshot.children {
print("Do whatever you need to do with the data")
}
} else {
print("DB no data!")
}
})
}
You can chain the queryOrder method call with queryLimited method together to retrieve the data. You don't have to be bothered with the userId wrapping your leaderboard datum, the queryOrdered(byChild: "score") method will look through everyone's score in the database and order them accordingly.

Chat app logic with Swift and Firebase

I'm implementing chat logic 1-1 in my iOS app. I'm using firebase to store chats and messages in chats. Messages are working great. But I have some difficulties with chats list.
My structure of stored chats looks like this
-Users
--UserID
---chatrooms
----chatroomID
-----updatedTime
-----deleted
-----read
-----receiverID
-----receiverName
I store in NSUserDefaults NSDate value "chatsLoadedTime". I query chats by sending query:
var time: String?
if defaults.valueForKey("chatsLoadedTime") != nil {
time = "\(defaults.valueForKey("chatsLoadedTime") as! NSDate)"
}
chatsRef.queryOrderedByChild("createdTime").queryStartingAtValue(time).observeEventType(.Value, withBlock: { snap in
defaults.setValue(toLocalTime(NSDate()), forKey: "chatsLoadedTime")
and so on
.
.
.
As far as I can see it is a good way to store chats and be able to download chats from different devices. For example I have just installed the app, my chatsLoadedTime value is nil, then I download all chats and update this value to current dateTime. If new chat created, then its createdTime is bigger then my chatsLoadedTime value and the app will download it.
But I don't know how to implement a good and efficient way to delete chat. I want this logic: if user1 delete chat, then he just change his value deleted=true. If second user also delete this chat at his side, then the chat and the messages will be totally removed from the app. If user1 deleted chat, but user2 not, then if user2 write to user1, then user1 receives this messages and all previous, because messages weren't deleted. But maybe it is not the best logic. Give m, please an advice how to do this in the best way.
If I could, I would want to query on multiple values, but as far as I know it is not possible.
I've added updatedTime value to every chatRoom. And I've added a value to device called lastLoadedTime. Every time when i receive snapshot from firebase i update lastLoadedTime to current time. So, I observe chats, where time value stored on the device is smaller than updatedTime in chatRoom in firebase. When i delete chat i set updated time to 1990-01-01 00:00:00. So i won't observe this chat until somebody send me a message. Ask me if you need more information to understand :)

iOS Parse query with mixed objects

I have a chat application working with Parse.com.
In my app users able to like posts. Like button has liked and normal states. So the next time when the user open the app I should show the posts that user liked with liked button state.
Server objects:
1) User
2) Post
3) Like
User object columns:
1. Likes(relation) - all liked posts by the user
2. Posts(relation) - all user posted posts
Post object columns:
1. Users(pointer) - created by user pointer
2. Likes(relation) - all existing likes on the post
Like object columns:
1. User(pointer) - liked by user
2. Post(pointer) - liked post
/**** PROBLEM ****/
Now I'm getting all posts from the "Posts" table, then getting the all liked posts from the "User's Liked posts relation" and if the post from the Posts table exists in the "User's Liked posts relation" I'm changing the Like button state to Liked.
I don't like how I'm doing it as now I'm calling two requests for showing the posts Like button state correct. I'm sure that it's possible to do with single request, but don't know how.
Can someone help me please ???
I think you do not need to use any data from your User class to deduce what you want. You can simply get the associated Likes data for each Post object in a single query. Then process the Likes information for each one and see if the current user is there to set the Like button state:
// Get all the posts
let postQuery = PFQuery(className: "Post")
// Include all like objects to for each post as well
postQuery.includeKey("Likes")
// execute the query
postQuery.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
// objects are all of the Post objects, and their associated Like objects, too
// Process data for each post and check the likes info to see if your current
// user is there, then set the Like button accordingly
}

updating many users at once in parse

I'd like to update user's column which presents related posts that the user might like,
my code is like that:
let users = query.findObjects() as [PFUser]
for user in users{
let rel = user.relationForKey("posts")
rel.addObject(post, forKey: "relatedPosts")
rel.saveInBackground()
}
I really don't know why, but I tried to do that in many versions (not only by relation, also by arrays and other methods) and it always updates just one user (the one who logged in)..
how can I get this done?
You can't update user that is not currently authenticated in your app.
The way I see it you have 2 options:
1) You can set some Cloud Code to run using the master key, so it can modify users.
2) You can add a new custom column to your Parse User class that will link to another class DB that has the related posts for the user.
take a look here: Parse.com Relations Guide
I would choose #2.

Resources