Value of String won't change in Swift despite clear declaration [duplicate] - ios

This question already has answers here:
Return Statement Error using Firebase in Swift
(1 answer)
Code Not Completing Before Next Method Is Called
(1 answer)
Swift accessing response from function
(1 answer)
Closed 4 years ago.
I am making an app in which I have to return a String representing the existence of a certain reference to my firebase realtime database (I am not using a Bool for other reasons). Despite executing the print commands directly above and below, when I print the function, the value of the String won't change and prints its default value "Unchanged".
My code:
func checkIfMovieHasAlreadyBeenShown(movieID: String) -> String {
var hasItBeenSeen = "Unchanged"
let uid = Auth.auth().currentUser?.uid
let ref = Database.database().reference()
ref.child("Seen Movies").child(uid!).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild(movieID){
print("The Result:")
self.changeStringHasItBeenSeen(YNString: "yes")
print("YES")
}
else {
print("The Result:")
self.changeStringHasItBeenSeen(YNString: "no")
print("NO")
}
})
return hasItBeenSeen
}
It is printed here:
print(checkIfMovieHasAlreadyBeenShown(movieID: "39"))
print(checkIfMovieHasAlreadyBeenShown(movieID: "38"))

observeSingleEvent is called in a closure. The return hasItBeenSeen line will not be affected by anything in the closure, as it will return first.
Imagine this: you tell your friend to go buy some apples. Before he has even left, you ask him for the price. Of course, he won't know, because he hasn't gone to buy the apples yet.
You need some way of performing something AFTER the closure has been called (or after the apples have been bought). Usually this can be done with a completion handler, where you pass a closure into your checkIfMovieHasAlreadyBeenShown function, such as, "do this AFTER you buy the apples".
Then you would call your function this way: checkIfMovieHasAlreadyBeenShown(movieID: "39", completion: { seen in print(seen) })

Related

What does "in" mean in Swift? [duplicate]

This question already has answers here:
Swift closure syntax using { ... in }
(2 answers)
Closed 1 year ago.
I've seen the word "in" multiple times in SwiftUI, but don't quite understand what it means. Because the word is so short and common, I'm having a hard time searching for its meaning and usage.
Most recently, I've seen it in the onChange method for example:
struct ContentView: View {
#State private var name = ""
var body: some View {
TextField("Enter your name:", text: $name)
.textFieldStyle(RoundedBorderTextFieldStyle())
.onChange(of: name) { newValue in
print("Name changed to \(name)!")
}
}
As like in your code example using in afther newValue let`s Swift and you know the return value of process is going happen on newValue so it is more like a symbol or flag called: "token" to you and to CPU that work is going happen on newValue because you put it before in, for example you gave name to onChange modifier with using newValue in, you will get updated value for name if get any new update, also note you can use $0 instead of newValue in and works same.
.onChange(of: name) { newValue in
return print("Name changed to \(name)!")
}
Or:
.onChange(of: name) {
print("Name changed to \($0)!")
}
Both are the same it depends how you like it.
Another example for better understanding:
let arrayOfInt: [Int] = [1, 2, 3]
let arrayOfString: [String] = arrayOfInt.map({ value in return value.description })
let arrayOfString: [String] = arrayOfInt.map({ $0.description })
For example we have an arrayOfInt and we want create a String array of it, for this work we can use map, and again like last example we used in in 2 way. in the first one as: value in and in the second on as $0. I recommend you work and learn more about closure in Swift/SwiftUI they are very important, without knowing them well, we cannot have good understanding of codes in iOS.
Also do not forget to attention to word of return right after in you can type and use or not use it, Swift is Smart enough to understand because of inference, but if you use it, it make your code much more easy and simple to read and better in syntax of code.

How to wait for firebase to load the data into a variable before returning the variable then call the function [duplicate]

This question already has answers here:
Wait for Firebase to load before returning from a function
(2 answers)
How to return value from an asynchronous callback function? [duplicate]
(3 answers)
Closed 5 years ago.
This Question is not a duplicate.
So I have a simple function that will take a parameter of the index of what tableview cell was clicked so the function will go into the didSelectRowAt tableView function, and it will also return a String.
The Function is called getOpposingUserName and it connects to firebase to gather the information provided inside the database.
Now I know that the functionality of the function works it is sort of a logic error that I don't quite understand. From what I know is that whenever you try to get data from firebase it runs that code and takes a second to actually get the data. but as its doing that it keeps running the code below where you tried to get data from the database.
which means if I want to return a String then the string will always be nil or whatever I set its initial value to, because the database will take to long to load and the function will already have ran through the code to return something.(that's my understanding of it, I'm sure there is a way more technical way to explain it)
so basically what I am asking is how would I "load" the data into a variable first then check that firebase has indeed loaded something into the variable and then return the variable for whatever called the function
This question IS not a duplicate I have read the question and tried to make sense of it in my own implementation. I am not returning a function that code is confusing to me when I read it.
here is my code for the function
func getOpposingUsername(_ index: Int) -> String {
var opposingUser: String = ""
//the self.tieBetToUser just gets the randomized id from the bet in firebase don't need to worry about it
self.datRef.child("Bets").child(self.tieBetToUser[index]).observe(.childAdded, with: { snapshot in
guard let dict = snapshot.value as? [String: AnyHashable] else {
return
}
opposingUser = dict["OpposingUsername"] as! String
print(opposingUser) //this code actually never runs I found out
})
//sleep(4) this will not work and is actually bad code anyway different phones may be faster or slower
return opposingUser // will always return "" at the moment
}
here is the solution the internet has provided
typealias someting = (String?) -> Void
func getOpposingUsername(completionHandler: #escaping someting, _ index: Int) {
var opposingUser: String = ""
self.datRef.child("Bets").child(self.tieBetToUser[index]).observe(.childAdded, with: { snapshot in
guard let dict = snapshot.value as? [String: AnyHashable] else {
return
}
opposingUser = dict["OpposingUsername"] as! String
if opposingUser.isEmpty {
completionHandler(nil)
} else {
completionHandler(opposingUser)
}
})
}
How exactly would I call this function somewhere else? like
getOpposingUsername(somethingGoesHere, index.path)

Asynchronous functions and Firebase with Swift 3

I read some questions about that. But I still have issues with asynchronous functions.
For example: I have a viewController1 where a button perform a segue to a viewController2. In the viewController2 class, I initialize some values in another class file named exampleClass. These values are retrieved from Firebase database or location values. These values need a little moment to be retrieved. I return thes values from the exampleClass into my viewController2. I print these values in the viewController2 viewDidLoad().
My issue: The device doesn't wait that the values are retrieved and execute following functions. Result: When I touch the button, printed values are nil values. It can also make the app crash if I don't secure the code.
What I've found so far: I learned that I only have to call a func at the end of a Firebase snapshot (for example) like this:
userRef.observeSingleEvent(of: .value, with: { (snapshot) -> Void in
self.name = snapshot.value as! String!
print(self.name)
self.forceEnd()
}) { (error) in
print(error.localizedDescription)
}
I named this function forceEnd to be clear. This is not working for me. I also tried to create handlers but no positive results.
My question: How can I force the device to wait for the values to be retrieved before performing the following question?
How can I force the device to wait for the values to be retrieved before performing the following question?
You don't want to force the device to wait, only need to perform some operations once these values are retrieved from Firebase database.
Performing an operation asynchronously can be done in multiple ways like blocks, protocols, notifications, etc.
Generally, blocks are the more elegant approach.
Some sample code can be like:
func myFirebaseNetworkDataRequest(finished: () -> Void) { // the function thats going to take a little moment
...
print("Doing something!") // firebase network request
finished()
}
// usage of above function can be as-
override func viewDidLoad() {
myFirebaseNetworkDataRequest {
// perform further operations here after data is fetched
print("Finally! It took a lot of moments to end but now I can do something else.")
}
}

How to use Nil in control flow with Firebase snapshots? (Swift) [duplicate]

This question already has answers here:
Checking if Firebase snapshot is equal to nil in Swift
(2 answers)
Closed 6 years ago.
I am trying to check to see if a key/value (TestKey/TestValue) exists at a node and if it does do A and if it does not do B.
I have found this similar thread regarding this on StackOverflow but none of the solutions seemed to work for me: Checking if Firebase snapshot is equal to nil in Swift
I have a database set up that already has the TestKey/TestValue key pair inserted.
Here is my code:
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
ref.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
//A print test to see stored value before control flow
print(snapshot.value!["TestKey"]) //returns TestValue
print(snapshot.value!["NilKey"]) //returns nil
// I change the value from TestKey <-> NilKey here to test the control flow
if snapshot.value!["TestKey"] != nil {
print("Exists currently")
} else {
print("Does not exist currently")
}
}) { (error) in
print(error.localizedDescription)
}
The problem is that only "A" is executed, even if the print test shows that the result is nil.
I have tried to reverse the operations with == nil and also have tried "is NSNull".
I cannot seem to get the control flow to work properly. Maybe one of the following is the culprit but I am unsure:
interaction of persistence with observesSingleEventOfType
not removing the observer (do I even need to for observeSingleEventType?)
something to do with optionals
something to do with asynchronous calls
something to do with the fact that firebase stores NSAnyObjects
Ultimately the goal is to check if a key/value pair exists and if it does exist: do nothing but if it does not exist: create it.
What am I doing wrong and how can I get this control flow to work?
Ok so after much fiddling I found a solution that works, it was indeed posted as this answer (https://stackoverflow.com/a/35682760/6515861) from the earlier existing question I linked (Checking if Firebase snapshot is equal to nil in Swift).
The way to make it work is to input the TestKey in the reference itself, remove the reference from snapshot.value("TestKey"), and then use "is Null".
Here is the working code:
// Database already exists with "TestKey"/"TestValue"
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
// replace "TestKey" with another string to test for nil
ref.child("TestKey").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
if snapshot.value is NSNull {
print("Does not exist currently")
} else {
print("Exists currently")
}
}) { (error) in
print(error.localizedDescription)
}
I am happy I found this solution but if anyone wants to still answer the original question I would love to know why it does not work the other ways.

Swift - Variable doesn't retain value

I am using AWS to host images for my iOS app. Right now I am trying to list all the objects in an S3 bucket.
Here is my code:
var description = ""
AWSS3.registerS3WithConfiguration(serviceConfig2, forKey: "s3")
AWSS3.S3ForKey("s3").listObjects(objectList).continueWithBlock { (task: AWSTask!) -> AnyObject! in
if task.error != nil {
println(task.error)
}
if task.result != nil {
description = task.result!.description
println(description)
}
return nil
}
println(description == "")
The output is true followed by the correct contents of task.result!.description. In other words, the println outside of the continueWithBlock is printing first and description has not been updated at that time.
How am I supposed to do things with description outside of the continueWithBlock?
You can assign a value that you need to another variable inside the scope of your class or function, then you can call didSet on the variable and carry out another function if you need to, like this:
var someVariableInScopeOfWhereItsNeeded = "abc" {
didSet {
self.maybeSomeOtherFunctionNow
}
}
You asked:
How am I supposed to do things with description outside of the
continueWithBlock
Short answer: You're not.
The whole point of an async method is that it continues immediately, before the time-consuming task has even begun processing. You put the code that depends on the results inside your block. See my answer on this thread for a detailed explanation, including an example project:
Why does Microsoft Azure (or Swift in general) fail to update a variable to return after a table query?
(Don't be fooled by the fact that it mentions MS Azure. It actually has nothing to do with Azure.)
#thefredelement 's solution of using a didSet method on the variable that gets set would work too.

Resources