Swift Firebase chat room - swift

I have chat app with possibility send messages one to one (fromId/toId). I want to upgrade it with chat rooms. How i can do that? What DB structure do i need for ChatingRoom? What else i need to do that?
My User.swift model:
import Foundation
import Firebase
class User: NSObject {
var id: String?
var name: String?
var login: String?
var email: String?
var profileImageUrl: String?
var role: String?
var isOnline: String?
init(dictionary: [String: AnyObject]) {
self.isOnline = dictionary["isOnline"] as? String
self.id = dictionary["userID"] as? String
self.name = dictionary["name"] as? String
self.login = dictionary["username"] as? String
self.email = dictionary["email"] as? String
self.profileImageUrl = dictionary["profileImageUrl"] as? String
self.role = dictionary["role"] as? String
}
}
Message.swift model:
import UIKit
import Firebase
class Message: NSObject {
var fromId: String?
var text: String?
var timestamp: NSNumber?
var toId: String?
var imageUrl: String?
var videoUrl: String?
var imageWidth: NSNumber?
var imageHeight: NSNumber?
init(dictionary: [String: Any]) {
self.fromId = dictionary["fromId"] as? String
self.text = dictionary["text"] as? String
self.toId = dictionary["toId"] as? String
self.timestamp = dictionary["timestamp"] as? NSNumber
self.imageUrl = dictionary["imageUrl"] as? String
self.videoUrl = dictionary["videoUrl"] as? String
self.imageWidth = dictionary["imageWidth"] as? NSNumber
self.imageHeight = dictionary["imageHeight"] as? NSNumber
}
func chatPartnerId() -> String? {
return fromId == Auth.auth().currentUser?.uid ? toId : fromId
}
}

Well currently you have From / To.
So To is going to be a room rather than a person.
Users will need to be able to join a room (or rooms) in order to see the messages that are sent to that room.
So your need a Rooms node.
If you have a messages node then you can just sort by To (room) instead of To (user) to get all the messages sent in that chat room. From will always be the User that wrote the message

Related

Unpacking Firestore array with objects to a model in swift

I have a project in swift with Firestore for the database. My firestore dataset of a user looks like this. User details with an array that contains objects.
I have a function that gets the specifick user with all firestore data:
func fetchUser(){
db.collection("users").document(currentUser!.uid)
.getDocument { (snapshot, error ) in
do {
if let document = snapshot {
let id = document.documentID
let firstName = document.get("firstName") as? String ?? ""
let secondName = document.get("secondName") as? String ?? ""
let imageUrl = document.get("imageUrl") as? String ?? ""
let joinedDate = document.get("joinedDate") as? String ?? ""
let coins = document.get("coins") as? Int ?? 0
let challenges = document.get("activeChallenges") as? [Challenge] ?? []
let imageLink = URL(string: imageUrl)
let imageData = try? Data(contentsOf: imageLink!)
let image = UIImage(data: imageData!) as UIImage?
let arrayWithNoOptionals = document.get("activeChallenges").flatMap { $0 }
print("array without opt", arrayWithNoOptionals)
self.user = Account(id: id, firstName: firstName, secondName: secondName, email: "", password: "", profileImage: image ?? UIImage(), joinedDate: joinedDate, coins: coins, activeChallenges: challenges)
}
else {
print("Document does not exist")
}
}
catch {
fatalError()
}
}
}
This is what the user model looks like:
class Account {
var id: String?
var firstName: String?
var secondName: String?
var email: String?
var password: String?
var profileImage: UIImage?
var coins: Int?
var joinedDate: String?
var activeChallenges: [Challenge]?
init(id: String, firstName: String,secondName: String, email: String, password: String, profileImage: UIImage, joinedDate: String, coins: Int, activeChallenges: [Challenge]) {
self.id = id
self.firstName = firstName
self.secondName = secondName
self.email = email
self.password = password
self.profileImage = profileImage
self.joinedDate = joinedDate
self.coins = coins
self.activeChallenges = activeChallenges
}
init() {
}
}
The problem is I don't understand how to map the activeChallenges from firestore to the array of the model. When I try : let challenges = document.get("activeChallenges") as? [Challenge] ?? []
The print contains an empty array, but when i do: let arrayWithNoOptionals = document.get("activeChallenges").flatMap { $0 } print("array without opt", arrayWithNoOptionals)
This is the output of the flatmap:
it returns an optional array
System can not know that activeChallenges is array of Challenge object. So, you need to cast it to key-value type (Dictionary) first, then map it to Challenge object
let challengesDict = document.get("activeChallenges") as? [Dictionary<String: Any>] ?? [[:]]
let challenges = challengesDict.map { challengeDict in
let challenge = Challenge()
challenge.challengeId = challengeDict["challengeId"] as? String
...
return challenge
}
This is the same way that you cast snapshot(document) to Account object

Change a value in my UserModel (class) based on a userid

I have a UserModel:
class UserModel {
var uid: String?
var username : String?
var email: String?
var profileImageUrl: String?
var dateOfBirth: String?
var registrationDate: Int?
var isFollowing: Bool?
var accessLevel: Int?
var onlineStatus: Bool?
init(dictionary: [String : Any]) {
uid = dictionary["uid"] as? String
username = dictionary["username"] as? String
email = dictionary["email"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
dateOfBirth = dictionary["dateOfBirth"] as? String
registrationDate = dictionary["userRegistrationDate"] as? Int
accessLevel = dictionary["accessLevel"] as? Int
onlineStatus = dictionary["onlineStatus"] as? Bool
}
}
And I also have a value like [12ih12isd89 : True]
I want to change the value "onlineStatus" for the user "12ih12isd89" to True and I thought the right way to do this is updateValue(:forKey:). But my class UserModel does not have updateValue(:forKey:).
How can I use this in my existing model?
Edit:
How I get the data:
func fetchAllUsers (completion: #escaping ([UserModel]) -> Void) {
let dispatchGroup = DispatchGroup()
var model = [UserModel]()
let db = Firestore.firestore()
let docRef = db.collection("users")
dispatchGroup.enter()
docRef.getDocuments { (querySnapshot, err) in
for document in querySnapshot!.documents {
let dic = document.data()
model.append(UserModel(dictionary: dic))
}
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
completion(model)
}
}
To me it looks like you need to find the right object in the array and update the property
let dict = ["12ih12isd89" : true]
var model = [UserModel]()
if let user = model.first(where: {$0.uid == dict.keys.first!}) {
user.onlineStatus = dict.values.first!
}
Depending on what ["12ih12isd89" : true] really is you might want to change the access from dict.keys.first! that I used
If your value dictionary contains more than one user, you can use a for loop like this:
var model = [UserModel]()
//Some initalization ...
let values = ["12ih12isd89" : true]
for (k, v) in values {
model.filter({$0.uid == k}).first?.onlineStatus = v
}

Sort a custom dictionary by commentDate

This is my data in Firestore:
My comment model:
class CommentModel {
var commentText: String?
var commentDate: NSObject?
var uid: String?
var username : String?
var profileImageUrl: String?
init(dictionary: [String: Any]) {
commentText = dictionary["commentText"] as? String
commentDate = dictionary["commentDate"] as? NSObject
uid = dictionary["uid"] as? String
username = dictionary["username"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
}
}
My comments dictionary contains all documents of "comments".
XCode gives me the following suggestion, but I am unsure how to use it.
self.comments.sorted { (CommentModel, CommentModel) -> Bool in
}
How can I sort my dictionary by the commentDate?
You need
self.comments.sort { $0.commentDate < $1.commentDate }
make sure comments is declared as var , also cast it as a Date or String
commentDate = dictionary["commentDate"] as? Date
OR
commentDate = dictionary["commentDate"] as? String

Swift 4 Firebase display group of users on TableView

How i can display users with unique role on TableView. For example i want to display users only with role "user", or only with role "guest".
my User model:
import Foundation
import Firebase
class User: NSObject {
var id: String?
var name: String?
var login: String?
var email: String?
var profileImageUrl: String?
var role: String?
var isOnline: String?
init(dictionary: [String: AnyObject]) {
self.isOnline = dictionary["isOnline"] as? String
self.id = dictionary["userID"] as? String
self.name = dictionary["name"] as? String
self.login = dictionary["username"] as? String
self.email = dictionary["email"] as? String
self.profileImageUrl = dictionary["profileImageUrl"] as? String
self.role = dictionary["role"] as? String
}
}
My Firebase user model:
You could simple filter the array of users by the role.
self.guests = self.users.filter({$0.role == "guest"})
Then display the list of guests just like any other UITableView.

want to fetch userdata from firebase into a dictionary

I hope you could help me:
I try to fetch userdata from a firebase database into a user class and call "setValuesForKeys". I do understand that my class properties have to be exactly the same as in the firebase dictionary, but I've got the error "this class is not key value coding-compliant for the key city."
func fetchUsers(){
Database.database().reference().child("users").observe(.childAdded, with: { (snapshot) in
if let usersDictionary = snapshot.value as? [String: String] {
let users = Userdata()
users.setValuesForKeys(usersDictionary)
}
}, withCancel: nil)
}
And my user class is
class Userdata: NSObject {
var email: String?
var password: String?
var firstname: String?
var lastname: String?
var street: String?
var streetno: String?
var zipcode: String?
var city: String?
var phone: String? }
The snapshot from firebase looks like
Snap (ndLBXXX75Oe9Y1PXrqfISL8A4v82) {
city = Washington;
email = "1#a.com";
firstname = Andre;
lastname = Doe;
password = xxxxxx;
phone = "";
street = "Mainstreet";
streetno = 1;
zipcode = 11111;
}
And the dictionary from the database looks like
["city": Washington, "firstname": Andre, "lastname": Doe, "email": 1#a.com, "password": xxxxxx, "streetno": 1, "phone": , "street": Mainstreet, "zipcode": 11111]
I have a solution so far by using:
users.city = dictionary["city"]
My question / problem: I do want to understand the problem behind the error message "this class is not key value coding-compliant for the key city." because the key at the class and in the firebase snapshot looks like the same.
Working solution: I had to extend my user class. Now, the whole user class looks like:
Here is the code :
import Foundation
class UserData: NSObject {
var id: String?
var email: String?
var password: String?
var salutation: String?
var degree: String?
var firstname: String?
var lastname: String?
var street: String?
var streetno: String?
var zipcode: String?
var city: String?
var phone: String?
var profileImage: String?
init(dictionary: [String: Any]) {
self.city = dictionary["city"] as? String
self.id = dictionary["id"] as? String
self.email = dictionary["email"] as? String
self.salutation = dictionary["salutation"] as? String
self.degree = dictionary["degree"] as? String
self.firstname = dictionary["firstname"] as? String
self.lastname = dictionary["lastname"] as? String
self.password = dictionary["password"] as? String
self.phone = dictionary["phone"] as? String
self.street = dictionary["street"] as? String
self.streetno = dictionary["streetno"] as? String
self.zipcode = dictionary["zipcode"] as? String
self.profileImage = dictionary["profileImage"] as? String
}
}