Assign a variable outside a string function in Swift 3 - ios

I am struggling for a while to assign the value returned by this function to a variable outside of it, in viewDidLoad but is returning an empty string.
getUid() - is returning the firebase uid
prRef - is calling the firebase table for users
Can someone tell me what am I doing wrong?
Thanks in advance,
var currentWorkplaceId: String?
func getCurrentWorkplaceId() -> String {
//completion:#escaping (Bool)->Void
var workplaceid = String()
prRef.child(getUid())
.child("workplace_id")
.observeSingleEvent(of: .value, with: { snapshot in
workplaceid = snapshot.value as! String
})
return workplaceid
}
usage:
currentWorkplaceId = getCurrentWorkplaceId()

observeSingleEvent is probably asynchronous with a callback. So at the time you call return, workplaceid hasn't been assigned yet. You need to do the same with your own callback.
func getCurrentWorkplaceId(_ completion: #escaping (_ workplaceId: String)->()) {
//completion:#escaping (Bool)->Void
prRef.child(getUid())
.child("workplace_id")
.observeSingleEvent(of: .value, with: { snapshot in
completion(snapshot.value as! String)
})
}
Usage:
getCurrentWorkplaceId() { workplaceId in
self.currentWorkplaceId = workplaceId
}

Related

Firebase observeSingleEvent completion handler

I have a FirebaseManager class and a get data function.
When calling this function, I need to get this data in the place from which it is called.
I am trying to do something like
import Firebase
class FirebaseManager {
var ref = Database.database().reference()
func getData (path: String) -> DataSnapshot {
self.ref.child(path).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
print(snapshot.value!)
return snapshot // XCODE ERROR - Cannot convert return expression of type 'Void' to return type 'DataSnapshot'
} else {
print("No data by path \(path)")
}
}
}
}
As your title suggests, what you'll need a completion handler. You can pass that in as a parameter to your function. It might look something like this:
func getData(path: String, completion: #escaping (DataSnapshot) -> Void) {
self.ref.child(path).observeSingleEvent(of: .value) { (snapshot) in
if snapshot.exists() {
completion(snapshot)
} else {
print("No data by path \(path)")
}
}
}
Then, you can call it like this:
getData(path: "myPath") { snapshot in
//do something with snapshot here
}
//no access to snapshot here
Note that you have access to snapshot inside the closure (the { }), so you can't access snapshot after the closure.
Personally, I'd probably refactor a bit so that you'd be returning the actual data than you want from inside the snapshot (like a String, a Dictionary, etc) rather than returning the DataSnapshot itself, but that's a matter of preference.

Returning data from function in Firebase observer code block swift

I'm new to firebase and I want to know if is any possible way to return data in observer block. I have class ApiManager:NSObject and in this class I want to create all my firebase function that will return some kind of data from database. This is one of my function in this class
func downloadDailyQuote() -> [String:String] {
let reference = Database.database().reference().child("daily")
reference.observeSingleEvent(of: .value) { (snap) in
return snap.value as! [String:String] //I want to return this
}
return ["":""] //I don't want to return this
}
And if I now do something like let value = ApiManager().downloadDailyQuote(), value contains empty dictionary. Is any solution for that?
Update: When you call .observeSingleEvent, you call the method asynchronously. This means that the method will start working, but the response will come later and will not block the main thread. You invoke this method, but there is no data yet and therefore you return an empty dictionary.
If you use the completion block, then you will get the data as soon as the method action is completed.
func downloadDailyQuote(completion: #escaping ([String:String]) -> Void) {
let reference = Database.database().reference().child("daily")
reference.observeSingleEvent(of: .value) { (snap) in
if let dictionaryWithData = snap.value as? [String:String] {
completion(dictionaryWithData)
} else {
completion(["" : ""])
}
}
}

Completion handler Firebase observer in Swift

I am making a completion handler for a function which will return a list of objects. When it return value for first time, it works well. But when any change happen into firebase database and again observe gets called, array size gets doubled up. Why it's getting doubled up?
func getStadiums(complition: #escaping ([Stadium]) -> Void){
var stadiums: [Stadium] = []
let stadiumRef = Database.database().reference().child("Stadium")
stadiumRef.observe(.value, with: { (snapshot) in
for snap in snapshot.children {
guard let stadiumSnap = snap as? DataSnapshot else {
print("Something wrong with Firebase DataSnapshot")
complition(stadiums)
return
}
let stadium = Stadium(snap: stadiumSnap)
stadiums.append(stadium)
}
complition(stadiums)
})
}
And calling like this
getStadiums(){ stadiums
print(stadiums.count) // count gets doubled up after every observe call
}
The code you're using declares stadiums outside of the observer. This means any time a change is made to the value of the database reference, you're appending the data onto stadiums without clearing what was there before. Make sure to remove the data from stadiums before appending the snapshots again:
func getStadiums(complition: #escaping ([Stadium]) -> Void){
var stadiums: [Stadium] = []
let stadiumRef = Database.database().reference().child("Stadium")
stadiumRef.observe(.value, with: { (snapshot) in
stadiums.removeAll() // start with an empty array
for snap in snapshot.children {
guard let stadiumSnap = snap as? DataSnapshot else {
print("Something wrong with Firebase DataSnapshot")
complition(stadiums)
return
}
let stadium = Stadium(snap: stadiumSnap)
stadiums.append(stadium)
}
complition(stadiums)
})
}
This line stadiumRef.observe(.value, with: { (snapshot) in ... actually adding an observer that will be called everytime your stadium data is changed.
Because you called it twice by using getStadiums(){ stadiums ..., the total observer added will be 2.
That makes the line stadiums.append(stadium) called twice in the second call.
My suggestion would be to use stadiumRef.observe() once without calling it from getStadiums().
Create a Model as below
class OrderListModel: NSObject {
var Order:String?
var Date:String?
}
Use the below code in the view controller and you should be able to see content in your tableview
func getOrdersData() {
self.orderListArr.removeAll()
let ref = Database.database().reference().child(“users”).child(user).child("Orders")
ref.observe(.childAdded, with: { (snapshot) in
print(snapshot)
guard let dictionary = snapshot.value as? [String : AnyObject] else {
return
}
let orderObj = OrderModel()
orderObj.Order = dictionary[“Order”] as? String
orderObj.Date = dictionary[“Date”] as? String
self.orderListArr.append(orderObj)
self.tableView.delegate = self
self.tableView.dataSource = self
self.tableView.reloadData()
}, withCancel: nil)
}
func ListenForChildrenAdded() {
let registerToListenTo = "YourPathHere"
ref.child(registerToListenTo).observeSingleEvent(of: .value) { (snapshot) in
let initialChildren = snapshot.childrenCount
var incrementer = 0
ref.child(registerToListenTo).observe(.childAdded, with: { (snapshot) in
incrementer += 1
print("snapshot: \(snapshot.key) #\(incrementer)")
if incrementer == initialChildren {
print("-> All children found")
} else if incrementer > initialChildren {
print("-> Child Was Added - Run Some Code Here")
}
})
}}

Add a completion to a function when firebase has finished, iOS, Swift

I'm trying to find out the best way to handle a completion on a function.
The function calls for data from firebase and adds them to an array of dictionaries. Because this is for maps and adding annotations the loop is adding lots of data before coming to the final appended version so its throwing loads of annotations dow in the same place. i want to know if i can call a completion on the loop when its finished and then call the function ShowSightings().
func getDatafromFB() {
DataService.ds.REF_POSTS.child("postCodes").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let postsIds = value?.allKeys as! [String]
for postId in postsIds {
let refToPost = Database.database().reference(withPath: "posts/" + "postCodes/" + postId)
refToPost.observe(.value, with: { snapshot in
if snapshot.exists() {
let postDict = snapshot.value as? [String: AnyObject]
print("Tony: before append post \(self.posts)")
self.posts.append(postDict!)
print("Tony: post \(self.posts)")
}else {
print("Tony: Couldn't get the data")
}
})
}
print("Tony: The compleetion result \(self.posts)")
})
}
You can try this:
func doAsyncTask(completionHandler:#escaping (Bool) -> ()){
//do async tasks
completionHandler(true) //<- call this when the data is retrieved
//so in your case, see below
}
override func viewDidLoad{
doAsyncTask(){ succes in
//succes gives true or false
}
}
//your case
}else {
print("Tony: Couldn't get the data")
}
completionHandler(true) //<- right there
This is for 1 async task. I see you want to use multiple async task. This is a job for dispatch groups. I change some of my function to take parameters. Check this out:
func doAsyncTask(postID: String, completionHandler:#escaping (Bool) -> ()){
//do async tasks
completionHandler(true)
}
override func viewDidLoad{
var arrPostIDs = [String]()
//append to arrPostIDs here
let postIDDispatchGroup = DispatchGroup()
for postID in arrPostIDs{
postIDDispatchGroup.enter()
doAsyncTask(postID: postID){ succes in
//succes gives true or false
postIDDispatchGroup.leave()
}
}
postIDDispatchGroup.notify(queue: .main) {
//everything completed :), do whatever you want
}
}

Swift Function returning a value from asynchronous firebase call

I am writing a function that takes a groupchatID (String) and returns a list of Recipients ([String]) for that group chat. I am struggling with the asynchronous part of the function however. When I run the function, it correctly prints to the console the array of usernames I was looking for. Although, when I call the function and try to print the returned value, it is always an empty array because the function returns the array before the firebase call has finished. I am trying to use a callback, but I do not quite understand the syntax of it all. Please take a look and let me know what needs to be changed.
The Function:
func GetRecipientsFor(GroupChatID :String , completion: #escaping ([String]) -> ()) {
var returnArray: [String] = [""]
rootRef.child("chatMembers").child(GroupChatID).observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children.allObjects {
var append = child as! FIRDataSnapshot
returnArray.append((append.key as String))
print("Return Array Currently Contains: \(returnArray)")
//The above printout works properly and when the for loop finishes, the array is exactly as I want it
}
completion(returnArray)
//BUT, this portion returns an empty array
})
}
How I call the function:
GetRecipientsFor(GroupChatID: gchatID) { (result) -> () in
print(result)
}
NEW Function Call
var recipients : [String] = [""]
DispatchQueue.main.async {
GetRecipientsFor(GroupChatID: gchatID) { result in
print(result) //PRINTS CORRECTLY!!!
recipients = result
}
}
print(recipients) //PRINTS A BLANK ARRAY
The problem with
var recipients : [String] = [""]
DispatchQueue.main.async {
GetRecipientsFor(GroupChatID: gchatID) { result in
print(result)
recipients = result
}
}
print(recipients) // Completes before recipients = result
is that the last line is happening before the async call.
To explain futher print(recipients) happens before recipients = result. All logic using recipients needs to happen within that completion block. All you need to do is
func getRecipients(completion: #escaping ([String]) -> ()) {
var recipients : [String] = [""]
DispatchQueue.main.async {
GetRecipientsFor(GroupChatID: gchatID) { result in
print(result)
completion(result)
}
}
}
if you want to have further logic included you can call a function within the completion i.e. handleResults(result). I think it would be very beneficial to read more about closures/completion blocks/and async calls.
You also can simplify that and use the firebase observer async task adding other param to your function like this:
//controller is where you need to get the result
func GetRecipientsFor(GroupChatID :String , controller: UIViewController){
rootRef.observeSingleEvent(of: .value) { (snapshot) in
//here you haver your snapshot. do the stuff and
controller.setDataForRecipe(dataFromYourSnapshot)
}
}
And in your controller:
public func setDataForRecipe (arrayIngredients: [String]){
//whatever you want. example:
self.data = arrayIngredients
self.tableView.reloadData()
}

Resources