Can not retrieve data correctly from Firebase - ios

This is my second version me trying to retrieve code from Firebase and do stuff with it. This is how I done it the second way:
channelRef?.observe(.childChanged, with: { (snapshot) -> Void in
let update = snapshot.value as! Dictionary<String, AnyObject>
var readyToGoValue: Bool?
var userID: String?
var amountOfPlayers: Int?
var changedCreator: String?
if let updatedReadyToGo = update["readyToGo"] as! Bool!{
if updatedReadyToGo == true
{
readyToGoValue = true
}
else
{
readyToGoValue = false
}
}
if let updateduserID = update["userID"] as! String!{
userID = updateduserID
}
if let updatedAmountOfPlayers = update["currentPlayers"] as! Int!{
amountOfPlayers = updatedAmountOfPlayers
}
if let updateduserID = update["userID"] as! String!{
userID = updateduserID
}
if let updatedCreator = update["creator"] as! String!{
changedCreator = updatedCreator
}
let currentUser = FIRAuth.auth()?.currentUser?.uid
if changedCreator != nil
{
print("changed creator")
self.creator = changedCreator!
}
This crashed with an error code:
Could not cast value of type '__NSCFString' (0x10a77f4a0) to 'NSDictionary' (0x10a780288). at the line " update". This was my first attempt:
channelRef?.observe(.childChanged, with: { (snapshot) -> Void in
let value = snapshot.value as? NSDictionary
let readyToGoValue = value?["readyToGo"] as? Bool ?? false
let userID = value?["userID"] as? String ?? ""
var amountOfPlayers = value?["currentPlayers"] as? Int ?? 0
let changedCreator = value?["creator"] as? String ?? ""
print(snapshot)
let currentUser = FIRAuth.auth()?.currentUser?.uid
print(changedCreator)
print(amountOfPlayers)
if changedCreator != ""
{
print("changed creator")
self.creator = changedCreator
}
This doesn't work swell. When changing the creator (just a string) in Firebase, I get this as a print when adding print(snapshot):
Snap (creator) hi
However the print("changed creator") never is executed. Why is this?
edit: This is how I got channelRef?:
super.prepare(for: segue, sender: sender)
if let channel = sender as? Channel {
let chatVc = segue.destination as! channelMultiplayerViewController
chatVc.channel = channel
chatVc.channelRef = channelRef.child(channel.id)
chatVc.usersKey = userKey
}
Printing more data:
print("path channel ref: " + "\(self.channelRef)")
print("snapshot: " + "\(snapshot)")
print("value: " + "\(value)")
-path channel ref: Optional(https://X.com/channels/-KeGKaJavH6uPYaSa7k4)
-snapshot: Snap (creator) new Creator
-value: nil
Update:
data structure:
This will work for now, but isn't there a better approach?:
if snapshot.key == "creator"
{
changedCreator = snapshot.value as! String
}
Another problem, exactly the same as above but with the solution for the first problem, this problem won't get solved. When I try to get the first child node, so the first user, and trying to get their userID, nothing works. I use this code:
let firstChild = UInt(1)
self.channelRef?.queryLimited(toFirst: firstChild).observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
print(snapshot)
print(value)
let newCreator = value?.value(forKey: "userID") as? String
if newCreator != nil{
print("Got the userID")
}
if snapshot.key == "userID"
{
print("Got the userID")
}
})
Snap (-KeJWMiXaL-FGp0J7b3u) {
"-KeJWO0V9kxgGnrACAtP" = {
PictureVersion = 2;
readyToGo = 0;
userID = SZlQ76RLCJQpFa0CDhrgFJoYzrs2;
username = pietje;
};
}
Optional({
"-KeJWO0V9kxgGnrACAtP" = {
PictureVersion = 2;
readyToGo = 0;
userID = SZlQ76RLCJQpFa0CDhrgFJoYzrs2;
username = pietje;
};
})
And this prints out, so no userID is given. Why is this? The userID is right there! I read the docs but it should work...

Try replacing chatVc.channelRef = channelRef.child(channel.id) with chatVc.channelRef = channelRef

Related

How can I guarantee that a Swift closure (referencing Firebase) fully executes before I move on?

I have multiple query snapshots with closures and some of them are using the data supplied by the query that came before it.
I have read up on GCD and I've tried to implement a DispatchGroup with .enter() and .leave() but I am apparently doing something wrong.
If somebody can help me by laying out exactly how to force one task to be performed before another, that would solve my problem.
If you can't tell, I am somewhat new to this so any help is greatly appreciated.
//MARK: Get all userActivities with distance(All Code)
static func getAllChallengesWithDistanceAllCode(activity:String, completion: #escaping ([Challenge]) -> Void) {
let db = Firestore.firestore()
let currUserID = Auth.auth().currentUser!.uid
var currUserName:String?
var distanceSetting:Int?
var senderAverage:Double?
var senderBestScore:Int?
var senderMatchesPlayed:Double?
var senderMatchesWon:Double?
var senderWinPercentage:Double?
var validUserActivities = [Challenge]()
db.collection("users").document(currUserID).getDocument { (snapshot, error) in
if error != nil || snapshot == nil {
return
}
currUserName = snapshot?.data()!["userName"] as? String
distanceSetting = snapshot?.data()!["distanceSetting"] as? Int
}
db.collection("userActivities").document(String(currUserID + activity)).getDocument { (snapshot, error) in
//check for error
//MARK: changed snapshot to shapshot!.data() below (possible debug tip)
if error != nil {
//is error or no data..??
return
}
if snapshot!.data() == nil {
return
}
//get profile from data proprty of snapshot
if let uActivity = snapshot!.data() {
senderBestScore = uActivity["bestScore"] as? Int
senderMatchesPlayed = uActivity["played"] as? Double
senderMatchesWon = uActivity["wins"] as? Double
senderAverage = uActivity["averageScore"] as? Double
senderWinPercentage = round((senderMatchesWon! / senderMatchesPlayed!) * 1000) / 10
}
}
if distanceSetting != nil {
db.collection("userActivities").whereField("activity", isEqualTo: activity).getDocuments { (snapshot, error) in
if error != nil {
print("something went wrong... \(String(describing: error?.localizedDescription))")
return
}
if snapshot == nil || snapshot?.documents.count == 0 {
print("something went wrong... \(String(describing: error?.localizedDescription))")
return
}
if snapshot != nil && error == nil {
let uActivitiesData = snapshot!.documents
for uActivity in uActivitiesData {
let userID = uActivity["userID"] as! String
UserService.determineDistance(otherUserID: userID) { (determinedDistance) in
if determinedDistance! <= distanceSetting! && userID != currUserID {
var x = Challenge()
//Sender
x.senderUserID = currUserID
x.senderUserName = currUserName
x.senderAverage = senderAverage
x.senderBestScore = senderBestScore
x.senderMatchesPlayed = senderMatchesPlayed
x.senderMatchesWon = senderMatchesWon
x.senderWinPercentage = senderWinPercentage
//Receiver
x.receiverUserID = userID
x.receiverUserName = uActivity["userName"] as? String
x.receiverAverage = uActivity["averageScore"] as? Double
x.receiverBestScore = uActivity["bestScore"] as? Int
if (uActivity["played"] as! Double) < 1 || (uActivity["played"] as? Double) == nil {
x.receiverMatchesPlayed = 0
x.receiverMatchesWon = 0
x.receiverWinPercentage = 0
} else {
x.receiverMatchesPlayed = uActivity["played"] as? Double
x.receiverMatchesWon = uActivity["wins"] as? Double
x.receiverWinPercentage = ((uActivity["wins"] as! Double) / (uActivity["played"] as! Double) * 1000).rounded() / 10
}
if uActivity["playStyle"] as? String == nil {
x.receiverPlayStyle = "~No PlayStyle~"
} else {
x.receiverPlayStyle = uActivity["playStyle"] as! String
}
x.activity = activity
//append to array
validUserActivities.append(x)
}
}
}
completion(validUserActivities)
}
}
}
}
try this
static func getAllChallengesWithDistanceAllCode(activity:String, completion: #escaping ([Challenge]) -> Void) {
let db = Firestore.firestore()
let currUserID = Auth.auth().currentUser!.uid
var currUserName:String?
var distanceSetting:Int?
var senderAverage:Double?
var senderBestScore:Int?
var senderMatchesPlayed:Double?
var senderMatchesWon:Double?
var senderWinPercentage:Double?
var validUserActivities = [Challenge]()
db.collection("users").document(currUserID).getDocument { (snapshot, error) in
if error != nil || snapshot == nil {
return
}
currUserName = snapshot?.data()!["userName"] as? String
distanceSetting = snapshot?.data()!["distanceSetting"] as? Int
db.collection("userActivities").document(String(currUserID + activity)).getDocument { (snapshot, error) in
//check for error
//MARK: changed snapshot to shapshot!.data() below (possible debug tip)
if error != nil {
//is error or no data..??
return
}
if snapshot!.data() == nil {
return
}
//get profile from data proprty of snapshot
if let uActivity = snapshot!.data() {
senderBestScore = uActivity["bestScore"] as? Int
senderMatchesPlayed = uActivity["played"] as? Double
senderMatchesWon = uActivity["wins"] as? Double
senderAverage = uActivity["averageScore"] as? Double
senderWinPercentage = round((senderMatchesWon! / senderMatchesPlayed!) * 1000) / 10
if snapshot != nil && error == nil {
let uActivitiesData = snapshot!.documents
for uActivity in uActivitiesData {
let userID = uActivity["userID"] as! String
UserService.determineDistance(otherUserID: userID) { (determinedDistance) in
if determinedDistance! <= distanceSetting! && userID != currUserID {
var x = Challenge()
//Sender
x.senderUserID = currUserID
x.senderUserName = currUserName
x.senderAverage = senderAverage
x.senderBestScore = senderBestScore
x.senderMatchesPlayed = senderMatchesPlayed
x.senderMatchesWon = senderMatchesWon
x.senderWinPercentage = senderWinPercentage
//Receiver
x.receiverUserID = userID
x.receiverUserName = uActivity["userName"] as? String
x.receiverAverage = uActivity["averageScore"] as? Double
x.receiverBestScore = uActivity["bestScore"] as? Int
if (uActivity["played"] as! Double) < 1 || (uActivity["played"] as? Double) == nil {
x.receiverMatchesPlayed = 0
x.receiverMatchesWon = 0
x.receiverWinPercentage = 0
} else {
x.receiverMatchesPlayed = uActivity["played"] as? Double
x.receiverMatchesWon = uActivity["wins"] as? Double
x.receiverWinPercentage = ((uActivity["wins"] as! Double) / (uActivity["played"] as! Double) * 1000).rounded() / 10
}
if uActivity["playStyle"] as? String == nil {
x.receiverPlayStyle = "~No PlayStyle~"
} else {
x.receiverPlayStyle = uActivity["playStyle"] as! String
}
x.activity = activity
//append to array
validUserActivities.append(x)
}
}
}
completion(validUserActivities)
}
}
}
}
}
}
}
I solved this by doing the following:
Add a constant before the for-in loop counting the total number of query results
let documentCount = snapshot?.documents.count
Add a counter before the for-in loop starting at 0
var runCounter = 0
Increment the counter with each iteration at the beginning of the for-in loop
runCounter += 1
Add code to the end of the for-in loop to call the completion handler to return the results
if runCounter == documentCount {
completion(validChallenges)
}

Efficient way to Insert and Update a core data object [2020]

Currently I am checking whether the object already exists in core data based on id and then updating and inserting. Is there any better way to do it? Have added "id" as a unique constraint, Which prevents inserting of objects with same "id". Does inserting just update the existing object with same id?
#nonobjc public class func saveUserMovies(movieJSON: [[String: Any]], user: UserProfile, isFavorites: Bool = false, isWatchlisted: Bool = false) {
let context = MMPersistentStore.sharedInstance.privateManagedObjectContext
for movie in movieJSON {
let movieID = movie["id"] as! Int
let fetchMovieWithIDRequest = fetchMovieRequest()
let moviePredicate = NSPredicate(format: "id == %d", movieID)
let sortDiscriptor = NSSortDescriptor(key: "id", ascending: false)
fetchMovieWithIDRequest.sortDescriptors = [sortDiscriptor]
fetchMovieWithIDRequest.predicate = moviePredicate
var userMovie: UserMovie?
context.performAndWait {
do {
userMovie = try fetchMovieWithIDRequest.execute().first
} catch {
print(MMErrorStrings.coreDataFetchError)
}
}
if let fetchedMovie = userMovie {
fetchedMovie.genreIds = movie["genre_ids"] as? [Int64]
fetchedMovie.adult = movie["adult"] as? Bool ?? false
if isFavorites {
fetchedMovie.isFavorite = isFavorites
} else {
fetchedMovie.isWatchlisted = isWatchlisted
}
fetchedMovie.video = movie["video"] as? Bool ?? false
fetchedMovie.backdropPath = movie["backdrop_path"] as? String
fetchedMovie.originalLanguage = movie["original_language"] as? String
fetchedMovie.originalTitle = movie["original_title"] as? String
fetchedMovie.overview = movie["overview"] as? String
fetchedMovie.posterPath = movie["poster_path"] as? String
fetchedMovie.releaseDate = movie["release_date"] as? String
fetchedMovie.releaseYear = String(fetchedMovie.releaseDate?.prefix(4) ?? "")
fetchedMovie.title = movie["title"] as? String
fetchedMovie.popularity = movie["popularity"] as? Double ?? 0.0
fetchedMovie.voteCount = movie["voteCount"] as? Int64 ?? 0
fetchedMovie.voteAverage = movie["voteAverage"] as? Double ?? 0.0
MMPersistentStore.sharedInstance.save(context: context)
} else {
let fetchedMovie = UserMovie(context: context)
fetchedMovie.id = movie["id"] as? Int64 ?? 0
fetchedMovie.user = user
fetchedMovie.genreIds = movie["genre_ids"] as? [Int64]
fetchedMovie.adult = movie["adult"] as? Bool ?? false
if isFavorites {
fetchedMovie.isFavorite = isFavorites
} else {
fetchedMovie.isWatchlisted = isWatchlisted
}
fetchedMovie.video = movie["video"] as? Bool ?? false
fetchedMovie.backdropPath = movie["backdrop_path"] as? String
fetchedMovie.originalLanguage = movie["original_language"] as? String
fetchedMovie.originalTitle = movie["original_title"] as? String
fetchedMovie.overview = movie["overview"] as? String
fetchedMovie.posterPath = movie["poster_path"] as? String
fetchedMovie.releaseDate = movie["release_date"] as? String
fetchedMovie.releaseYear = String(fetchedMovie.releaseDate?.prefix(4) ?? "")
fetchedMovie.title = movie["title"] as? String
fetchedMovie.popularity = movie["popularity"] as? Double ?? 0.0
fetchedMovie.voteCount = movie["voteCount"] as? Int64 ?? 0
fetchedMovie.voteAverage = movie["voteAverage"] as? Double ?? 0.0
MMPersistentStore.sharedInstance.save(context: context)
}
}
}
}
Have added "id" as a unique constraint, Which prevents inserting of objects with same "id".
I didn't use it before yet
Does inserting just update the existing object with same id?
No, it'll insert the new object.
For your case, you could make a refactoring, please refer the findOrCreate function in https://github.com/objcio/core-data/blob/master/SharedCode/Managed.swift
It'll help you avoid duplicated code.
One more thing, your request doesn't need the sortDescriptor, and it should have the limit = 1, and returnObjectAsFaults = false for optimisation.
After that, you just need to make sure your function is called in the same context to avoid duplications.

Swift sender UIButton change text

I have a follow/unfollow button and am accessing it by "sender". I am changing the text when the user taps it to follow or unfollow another user. Problem is that when it should show "unfollow" it is showing the default text used in storyboard. The button changes to "follow" as it should, but not "unfollow". Also, I have to use "sender: UIButton" because I am accessing the tableview cells "tag" for the right information.
#IBAction func followButton(_ sender: UIButton) {
//self.yourFollowing.removeAll()
//self.following.removeAll()
self.followingTableView.reloadData()
let accessData = self.yourFollowing[sender.tag].dataPass
let businessUid = accessData["uid"] as! String
let businessName = accessData["businessName"] as! String
let businessStreet = accessData["businessStreet"] as! String
let businessCity = accessData["businessCity"] as! String
let businessState = accessData["businessState"] as! String
let businessZip = accessData["businessZIP"] as! String
let businessPhone = accessData["businessPhone"] as! String
let businessLatitude = accessData["businessLatitude"] as! String
let businessLongitude = accessData["businessLongitude"] as! String
let businessWebsite = accessData["businessWebsite"] as! String
let businessFacebook = accessData["facebookURL"] as! String
let businessFoursquare = accessData["foursquareURL"] as! String
let businessGoogle = accessData["googleURL"] as! String
let businessInstagram = accessData["instagramURL"] as! String
let businessSnapchat = accessData["snapchatURL"] as! String
let businessTwitter = accessData["twitterURL"] as! String
let businessYelp = accessData["yelpURL"] as! String
let userID = Auth.auth().currentUser!.uid
let ref = Database.database().reference()
let key = ref.child("Businesses").childByAutoId().key
var isFollower = false
let followersRef = "followers/\(businessUid)/\(self.loggedInUserData?["uid"] as! String)"
let followingRef = "following/" + (self.loggedInUserData?["uid"] as! String) + "/" + (businessUid)
ref.child("Businesses").child(userID).child("following").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
if let following = snapshot.value as? [String : AnyObject] {
for (item, value) in following {
if value as! String == businessUid {
isFollower = true
let followersRef = "followers/\(businessUid)/\(self.loggedInUserData?["uid"] as! String)"
let followingRef = "following/" + (self.loggedInUserData?["uid"] as! String) + "/" + (businessUid)
let childUpdates = [followingRef:NSNull(),followersRef:NSNull()]
self.databaseRef.updateChildValues(childUpdates)
ref.child("Businesses").child(userID).child("following/\(item)").removeValue()
ref.child("Businesses").child(businessUid).child("followers/\(item)").removeValue()
sender.titleLabel?.text = "follow"
//self.yourFollowing.removeAll()
self.following.removeAll()
self.followingTableView.reloadData()
}
}
}
// Follow
if !isFollower {
sender.titleLabel?.text = "unfollow"
let followersData = ["email":self.loggedInUserData?["email"] as! String, "businessName":self.loggedInUserData?["businessName"] as! String]
let followingData = ["businessName":businessName, "businessStreet":businessStreet, "businessCity":businessCity, "businessState":businessState, "businessZIP":businessZip, "businessPhone":businessPhone, "businessWebsite":businessWebsite,"businessLatitude":businessLatitude, "businessLongitude":businessLongitude, "facebookURL":businessFacebook, "twitterURL":businessTwitter, "instagramURL":businessInstagram, "googleURL":businessGoogle, "yelpURL":businessYelp, "foursquareURL":businessFoursquare, "snapchatURL":businessSnapchat, "uid":businessUid]
let childUpdates = [followersRef:followersData, followingRef:followingData]
self.databaseRef.updateChildValues(childUpdates)
let following = ["following/\(key)" : businessUid]
let followers = ["followers/\(key)" : userID]
ref.child("Businesses").child(userID).updateChildValues(following as Any as! [AnyHashable : Any])
ref.child("Businesses").child(businessUid).updateChildValues(followers)
self.yourFollowing.removeAll()
self.following.removeAll()
self.followingTableView.reloadData()
}
})
}
Your problem is this line in your button action
#IBAction func followButton(_ sender: UIButton) {
.
.
var isFollower = false
.
.
}
You are declaring the variable isFollow inside the button action. This means that every time regardless of follow or unfollow, your isFollower is false which is why the condition for follow will work. But the change to true which is inside the completion of follow won't reflect next time you click the button because you are resetting isFollower to false.
Solution: Move the variable isFollow outside the button action.
var isFollow = false
#IBAction func followButton(_ sender: UIButton) {
// Your logic
}
Also your logic inside the completion seems wrong. Something like the code below should be there to change it to false.
if value as! String == businessUid {
isFollower = !isFollower
if isFollower {
// Follow logic
sender.setTitle("unfollow", for: .normal)
} else {
// Unfollowed logic
sender.setTitle("follow", for: .normal)
}
// Reload table
}

Swift firebase timestamp cant show

I am able to retrieve and convert my timestamp that I got from firebase(i did a breakpoint at that line and my time shows 5/3/18,3:05 PM, so it's fine), below is my code:
func loadMsg() {
let toId = user!.id!
let fromId = Auth.auth().currentUser!.uid
let chatRoomId = (fromId < toId) ? fromId + "_" + toId : toId + "_" + fromId
let ref = Database.database().reference().child("privateMessages").child(chatRoomId)
ref.observe(.value) { (snapshot) in
ref.observeSingleEvent(of: .childAdded, with: { (datasnap) in
let lastMsgTime = (datasnap.value as! [String: AnyObject])["timestamp"] as? Double
// to get timestamp and convert to date and time
let x = lastMsgTime!
let date = NSDate(timeIntervalSince1970: x)
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
let time = formatter.string(from: date as Date)
self.message.timestamp = time //HERE IT CRASHES!!!!!
if let snapshot = snapshot.children.allObjects as? [DataSnapshot] {
self.messages.removeAll()
for data in snapshot {
let newMsg = Message(dictionary: data.value as! [String: AnyObject])
self.messages.append(newMsg)
}
}
})
DispatchQueue.main.async {self.tableView.reloadData()}
}
}
However it crashes at the line which i commented above, i was supposed to show it on my tableviewCell in my message cell. My tableView cell was done by xib which looks like :
and the code of my tableviewCell would be:
{
self.message = message
if message.fromId == currentUser {
sentView.isHidden = false
sentMsgLabel.text = message.textMessages
receivedMsgLabel.text = ""
receivedView.isHidden = true
timeReceived.text = message.timestamp
timeSent.text = message.timestamp
} else {
sentView.isHidden = true
sentMsgLabel.text = ""
receivedMsgLabel.text = message.textMessages
receivedMsgLabel.isHidden = false
timeReceived.text = message.timestamp
timeSent.text = message.timestamp
}
}
So, why would it crash and says
"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an
Optional value"
my message class code:
class Message: NSObject {
var fromId: String?
var textMessages: String?
var timestamp: String?
var toId: String?
var message: Message!
var _messageKey: String!
init(dictionary: [String: Any]) {
self.fromId = dictionary["fromId"] as? String
self.textMessages = dictionary["textMessages"] as? String
self.toId = dictionary["toId"] as? String
self.timestamp = dictionary["timestamp"] as? String
}
init(messageKey: String, postData: Dictionary<String, AnyObject>) {
_messageKey = messageKey
if let message = postData["textMessages"] as? String {
textMessages = message
}
if let sender = postData["fromId"] as? String {
fromId = sender
}
}
}
I believe there isnt any issue with my firebase, main problem is at the tableview cell, why wouldnt it show?
Try to get timestamp from server like this:
ServerValue.timestamp()

Getting Value From FirebaseDB

I am trying to get values from my firebasedb, on run, ref.observeSingleEvent(of: .value, with: { snapshot in causes fatal error unexpectedly found nil while unwrapping an Optional value. As im sure you can tell... I have no idea what im doing... Thank you in advance...
func geths() -> Int{
var sch:Int = 0
var nam:String = ""
print("start geths")
ref.observeSingleEvent(of: .value, with: { snapshot in
if (snapshot.exists()){
print("snapexist")
if let snapval = snapshot.value as? [String:AnyObject]{
let hs = snapval["hs"] as? String
let name = snapval["name"] as? String
self.hso = hs!
self.nameo = name!
nam = self.nameo
if let myNumber = NumberFormatter().number(from: self.hso) {
let i = myNumber.intValue
sch = i
}else{
sch = 0
}
}else{
print("error")
}
}else{
print("error")
}
})
return sch
}
EDIT************
still dont work :( same errors
func geths() -> Int{
var sch:Int = 0
var nam:String = ""
print("start geths")
ref.observeSingleEvent(of: .value, with: { (snapshot) in
if (snapshot.exists()){
print("snapexist")
let snapval = snapshot.value as? NSDictionary
let hs = snapval?["hs"] as? String ?? ""
let name = snapval?["name"] as? String ?? ""
if (hs != nil){
self.hso = hs
}else{
self.hso = "0"
}
if (name != nil){
self.nameo = name
}else{
self.nameo = "bob"
}
nam = self.nameo
if let myNumber = NumberFormatter().number(from: self.hso) {
let i = myNumber.intValue
sch = i
}else{
sch = 0
}
}else{
print("error")
}
})
return sch
}
create firebase reference like this :
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
for more details go though this link :
https://firebase.google.com/docs/database/ios/read-and-write
You must initialize the ref variable, you was only declare it by
var ref: FIRDatabaseReference!
For example
var ref: FIRDatabaseReference! = FIRDatabase.database().reference(withPath: "hs")
You may read example about using Firebase here:
https://www.raywenderlich.com/139322/firebase-tutorial-getting-started-2

Resources