I have an array of SKU numbers that I'm returning from Google Firestore "[428024, 4298212]".
I have written a function assigning the array of SKU's to a variable, but I am lost as to how to return that variable from the function.
let db = Firestore.firestore()
func getItems() -> [Int] {
let userID = Auth.auth().currentUser?.uid ?? "nil"
if (session.session != nil) {
self.data.removeAll()
db.collection("users").document(userID).getDocument { (document, error) in
if let document = document, document.exists {
let itemID = document.get("items") as! Array<Int>
print(itemID as Any)
// Prints "[428024, 4298212]"
return itemID
} else {
print("Document does not exist")
}
}
}
}
I'm getting the error "Unexpected non-void return value in void function, though I can see that the array of SKU's are being returned when it runs the "print(itemID as Any)" line.
Is there any mistake in how I have the function written?
Querying the document through Firestore is written using a completion handler and trying to return any value from within this handler to your original function will deliver this error. Instead, you'll need to adjust your original function getItems() to account for this as such:
let db = Firestore.firestore()
#State private var itemIDs: [Int] = []
func getItems(completion: #escaping (_ itemIDs: [Int]?) -> ()) {
let userID = Auth.auth().currentUser?.uid ?? "nil"
if (session.session != nil) {
self.data.removeAll()
db.collection("users").document(userID).getDocument { (document, error) in
if let document = document, document.exists {
let itemIDs = document.get("items") as! Array<Int>
completion(itemIDs) // call completion handler to return value
} else {
print("Document does not exist")
}
}
}
}
func callingYourFunction() {
self.getItems() { itemIDs in
if let ids = itemIDs {
// itemIDs exists -> do whatever else you originally intended to do with the ids
self.itemIDs = ids
}
}
}
Take a look at here if you want to learn more about closures and completion handlers! Hopefully this helps.
Related
I want to save a particular field from a document into a variable. My code so far:
func getDocument(path: String, field: String? = "nil") -> some Any{
var returnVar : Any = "DEFAULT VAL"
var db: Firestore!
db = Firestore.firestore()
let docRef = db.document(path)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
if(field != "nil"){
let property = document.get("phrase") ?? "nil"
returnVar = property
return;
}
else{
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
returnVar = dataDescription
return;
}
} else {
print("Document does not exist")
returnVar = -1
return;
}
}
print("Returned val: " + (returnVar as! String))
return returnVar;
}
It seems however that my getDocument method returns before ever reading the data from firebase(coming from pure OOP land, I have no clue how this even happens) From debugging, it seems execution simply skips over the entire docRef.getDocument code and jumps to the return statement. It is only after the function returns does the code in the docRef.getDocument block get executed(what? how does code in a function that has already returned continue to execute?).
How can I store a particular field in a variable and return it?
That's because Firestore function getDocument is an asynchronous function and it returns immediately and after that it continues to execute the code inside it. If you want to return a particular value from here, you need to use completion Handler. Your function may look like this.
func getDocument(path: String, field: String? = "nil", completion:#escaping(Any)->()) {
var returnVar : Any = "DEFAULT VAL"
var db: Firestore!
db = Firestore.firestore()
let docRef = db.document(path)
docRef.getDocument { (document, error) in
if let document = document, document.exists {
if(field != "nil"){
let property = document.get("phrase") ?? "nil"
returnVar = property
completion(returnVar)
}
else{
let dataDescription = document.data().map(String.init(describing:)) ?? "nil"
returnVar = dataDescription
completion(returnVar)
}
} else {
print("Document does not exist")
returnVar = -1
completion(returnVar)
}
}
}
Then call the function in viewDidLoad or in any other function like this.
getDocument(path: path, field: field){(value) in
print(value)
}
You can checkout more about Completion Handlers here
I understand that Firestore loads data asynchronously, but I want to use these data later and in different ViewControllers. Is there any possibility, to save data in array?
func findPlayers (filters: Dictionary<String, Any>) -> [String] {
let reference = dataService.instance.dbF.collection("playersStats")
var query1: Query
var keysArray = [String?] ()
var resultIDs = [String] ()
for key in filters.keys {
if key != "PositionName" {
keysArray.append(key)
}
}
if filters.keys.count == 1 {
if keysArray[0] != nil {
let value = filters[keysArray[0]!] as? Double
query1 = reference.whereField(keysArray[0]!, isGreaterThan: value! )
query1.getDocuments{ (snapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in snapshot!.documents {
let id = String(document.documentID)
resultIDs.append(id)
}
}
}
}
}
return resultIDs
}
I really expect to see array full of data I want to get.
Hello i try to parse a array of strings into a dao. To do so i created this:
func getUsersAbos(){
let userid = Auth.auth().currentUser?.uid
let docRef = db.collection("Users").document(userid!)
docRef.getDocument { (document, error) in
if let city = document.flatMap({
$0.data().flatMap({ (data) in
return UserBlogObject(channelAbos: data)
})
}) {
print("City: \(city)")
} else {
print("Document does not exist")
}
}
}
and here is my Dao:
import Foundation
class UserBlogObject{
var channelAbos = Any
init(channelAbos: [String]) {
self.channelAbos = channelAbos
}
init(){
}
}
i get an error at this line:
return UserBlogObject(channelAbos: data)
Cannot convert value of type '[String : Any]' to expected argument type '[String]'
please note that i want to download just an array inside the document, not the whole document.
You can do something like this. I don't think there is a need to overcomplicate the function with flatMap() but you can do it if you want. Here is a code example of how you could create your method.
func getUsersAbos() {
guard let userID = Auth.auth().currentUser?.uid else { return }
let docRef = db.collection("Users").document(userID)
docRef.getDocument { (document, error) in
if error != nil { return }
guard let data = document?.data() else { return }
guard let channelAbos = data["channelAbos"] as? [String] else { return }
let userBlogObject = UserBlogObject.init(channelAbos: channelAbos) // This is the created object. Handle it.
}
}
And your class:
class UserBlogObject {
var channelAbos : [String]
init(channelAbos: [String]) {
self.channelAbos = channelAbos
}
}
I'm trying to run a function to be able to retrieve data from the realtime database with Firebase, however whenever I run the function; the observerSingleEvent part of my function will not run, I have tried putting a print statement within and it is not being run nor is the fields being read to the variable, any help would be beneficial.
func checkIfNewDay() -> Bool {
print(self.currDate)
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
let userID = FIRAuth.auth()?.currentUser?.uid
print("outside function")
ref.child("user").child(userID!).child("dates").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
print("inside function")
let value = snapshot.value as? NSDictionary
print("just to make sure its going inside the function. Delete after")
self.lastDate = value?["lastSaveDate"] as? String ?? "Date Invalid"
self.newLastDate = String(self.lastDate)
if self.newLastDate != "Date Invalid" {
print(self.lastDate)
} else {
print("Error, date not able to be recieved from the database")
self.catchGetDateError = true
self.saveCurrentDate()
}
})
if (!self.catchGetDateError) {
print(self.newLastDate, "newLastDate")
print(self.currentDate, "currentDate")
if (self.newLastDate == self.currentDate) {
print("Day has not moved on.")
return false
} else {
print("Day has moved on!")
return true
}
}
return true
}
I apologise for the really long function - was quite a weird one to write.
From comments I think I have understood, what do you want.
For getting this results like sync, you need to implement escaping. Like this:
func checkIfNewDay(completion: #escaping (_ isNew: Bool) -> Void) {
print(self.currDate)
var ref: FIRDatabaseReference!
ref = FIRDatabase.database().reference()
let userID = FIRAuth.auth()?.currentUser?.uid
print("outside function")
ref.child("user").child(userID!).child("dates").observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
print("inside function")
let value = snapshot.value as? NSDictionary
print("just to make sure its going inside the function. Delete after")
self.lastDate = value?["lastSaveDate"] as? String ?? "Date Invalid"
self.newLastDate = String(self.lastDate)
if self.newLastDate != "Date Invalid" {
print(self.lastDate)
if (self.newLastDate == self.currentDate) {
print("Day has not moved on.")
completion(false)
} else {
print("Day has moved on!")
completion(true)
}
} else {
print("Error, date not able to be recieved from the database")
self.catchGetDateError = true
self.saveCurrentDate()
completion(false)
}
})
}
So, now you can use your func:
checkIfNewDay(completion: { isNew in
// do whatever you want. isNew will have info, that you need.
})
You should have it, because .observe functions work async. You should understand the idea.
Hope it helps.
func getParse (className:String,key:String,dataName:AnyObject) -> (String)
{
var result = String()
var query = PFQuery(className: className)
query.whereKey(key, equalTo: dataName)
query.findObjectsInBackgroundWithBlock{
(objects, error) -> Void in
if error == nil {
println("Found")
if let objects = objects as? [PFObject] {
for object in objects {
result = object[key] as! String
}
}
} else {
println("Error \(error) \(error!.userInfo!)")
}
}
return result
}
This is my function that can getting data from my class in parse database. I want to return that data in String but it returned nothing when I try to printed it.
Thank you for every comments.
You are using an asynchronous call. You need to use findObjects(), but this will stay on the main thread. Why do you need to return a string? You could set a variable from within the completion block that could update a label on your view or something like that.
Edit: Since you are trying to set a label, you don't need to return the string, you should just set the label from within your completion block. This would modify you're given code as follows:
func getParse (className:String,key:String,dataName:AnyObject)
{
var result = String()
var query = PFQuery(className: className)
query.whereKey(key, equalTo: dataName)
query.findObjectsInBackgroundWithBlock{
(objects, error) -> Void in
if error == nil {
println("Found")
if let objects = objects as? [PFObject] {
for object in objects {
// result = object[key] as! String
self.textLabel.text = result // NEW CODE HERE
}
}
} else {
println("Error \(error) \(error!.userInfo!)")
}
}
}