I am new to Swift and I am wondering why the array is empty at the end of the function at the print("array printed2: \(newArr)") line, when at the first print statement print("array printed1: \(newArr)") it is filled with data.
Does anyone have any corrections or suggestions on how to make the data in the array stay?
Thanks in advance, the code for the function is below.
func testRdArr(){
var newArr=[Establishment]()
print("Downloading club data...")
let db = Firestore.firestore()
db.collection("clubs").getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
} else {
for document in querySnapshot!.documents {
let data = document.data()
let name = data["Name"] as? String ?? "Name Unknown"
let description = data["description"] as? String ?? "Description Uknown"
let imageURL1 = data["imgUrl"] as? String ?? "Image Unknown"
let coordX = data["coordX"] as? String ?? "Unknown"
let coordY = data["coordY"] as? String ?? "Unknown"
let openingTimes=data["openingTimes"] as? [String] ?? ["Unknown"]
let weeklyEvents=data["weekly events"] as? [String] ?? ["Unknown"]
let postcode=data["postcode"] as? String ?? "Unknown"
let id = document.documentID
let x = Establishment(name: name, imageURL1: imageURL1, coordX: coordX, coordY: coordY, postcode: postcode, openingTimes: openingTimes, weeklyEvents: weeklyEvents, description: description, type: "club", id: id)
newArr.append(x)
}
}
//line below prints the populated array
print("array printed1: \(newArr)")
print("end of printing array")
}
//at this point array becomes empty, why does this happen and how would you suggest
//fixing it?
print("array printed2: \(newArr)")
}
The download happens in the background and completes after your print at the bottom of the function. It did not become empty. That line happened before the download was done.
See: Waiting for Asynchronous function call to complete
Related
I'm getting duplicate results even after emptying my array first can someone please explain why is this happening I have tried to empty this array DriverOffers in so many different places but still getting the same result duplicate tableView cells everywhere.
func newOrders(){
//self.DriverOffers = [] here same result
let fireStore = Firestore.firestore()
let doc = fireStore.collection("الطلبات")
doc.addSnapshotListener { (query, err) in
if err != nil {
print(err?.localizedDescription ?? "")
}
//self.DriverOffers = []
query?.documentChanges.forEach({ (change) in
//self.DriverOffers = []
switch change.type{
case .added:
for document in query!.documents{
self.DriverOffers = []
let snap = document.data()
let name = snap["name"] as? String ?? ""
let phone = snap["phone"] as? String ?? ""
let time = snap["time"] as? String ?? ""
let marketName = snap["marketName"] as? String ?? "موقع محدد"
let price = snap["amount"] as? String ?? ""
DispatchQueue.main.async {
//self.DriverOffers = []
let offer = driverOrdersData(userUid: userid, name: name, phone: phone, amount: price)
self.DriverOffers.append(offer)
self.DriverOrdersTV.reloadData()
}
}
}
case .removed:
print("removed")
case .modified:
break
}
})
}
Use this method inside the tableViewCell and check.
override func prepareForReuse() {
super.prepareForReuse()
self.nameLabel.text = nil
}
in my project i'm try to implement a social app including a friends request using cloud firebase
I can't find a solution to avoid the fire base listener duplicate the record!
at the first opening, the list is correct and update, but if for any reason I add a friends the listener duplicate the record.
if I check my database Firestore the record is only one there is no double record.
if I exit and go back to the view all the record are ok.
here a picture of what happen when I add friends.
I attach my part of my code:
func testUpdatePendingUser(userLoggato: UserModel, utenteADDED: #escaping (UserModel)->(), vettoreUser: #escaping ([UserModel])->()) {
db.collection("user").document(userLoggato.userID).collection("pendingFriends")
.addSnapshotListener(includeMetadataChanges: false) { documentSnapshot, error in
var arrayUtentiStart = [UserModel]()
guard let snapshot = documentSnapshot else { return }
snapshot.documentChanges.forEach { (documentChange) in
switch documentChange.type {
case .added :
debugPrint("add")
let dict = documentChange.document.data()
let name = dict["name"] as? String ?? "na name"
let surname = dict["surname"] as? String ?? "na name"
let email = dict["email"] as? String ?? "na name"
let userLevel = dict["adminLevel"] as? String ?? "unable to get admin level"
let idUser = dict["userID"] as? String ?? "no ID"
let position1 = dict["position"] as? String ?? "na preferance position"
let position2 = dict["position2"] as? String ?? "na preferance position"
let vote = dict["vote"] as? Int ?? 0
self.downloadImageForAdmin(userID: idUser) { (urlImage) in
let utente = UserModel(name: name, surname: surname, email: email, userID: idUser, adminLevel: userLevel, immagine: urlImage, position: position1, position2: position2, vote: vote)
utenteADDED(utente)
arrayUtentiStart.append(utente)
vettoreUser(arrayUtentiStart)
}
case .modified :
debugPrint("mod")
case .removed :
debugPrint("rem")
}
}
}
}
this to be use on the contentView
func newPendinguser(userLoggato: UserModel){
usersPendingNEW = []
testUpdatePendingUser(userLoggato: userLoggato, utenteADDED: { (utenteCambiato) in
if !self.usersPendingNEW.isEmpty { // case is not empty , guess problem is here!!
self.usersPendingNEW.append(utenteCambiato)
}
}) { (vettoreIniziale) in
if self.usersPendingNEW.isEmpty{ // first view appear
self.usersPendingNEW = vettoreIniziale
}
}
}
i think this code will be duplicate your data.
utenteADDED(utente)
arrayUtentiStart.append(utente)
vettoreUser(arrayUtentiStart)
you design utenteADDED(utente) run when first time init, but when you have data in array, that code still run.
Check first run before run this code:
utenteADDED(utente)
Clear the array which is presenting the view and then assign the list received via listener
Hi this function is producing an unexpected result for me. I'm new to programming so I appreciate any help understanding this.
The first print statement prints second to the last print statement. I think its because maybe the call to Firebase is still waiting for results even though the function completed the operation? How can I make it so that the function doesn't return an empty array?
func readFromDatabase() -> [[String]] {
let db = Firestore.firestore()
db.collection("Letters").getDocuments { (snapshot, error) in
if error != nil {
print(error?.localizedDescription ?? "error")
} else {
for document in (snapshot?.documents)! {
// let fetchedData = document.data() //gets all data from all documenters unorganized
let firstLetter = document.get("1") as! String
let secondLetter = document.get("2") as! String
let thirdLetter = document.get("3") as! String
let fourthLetter = document.get("4") as! String
let fifthLetter = document.get("5") as! String
let sixthLetter = document.get("6") as! String
let seventhLetter = document.get("7") as! String
let eighthLetter = document.get("8") as! String
let organizedData = [firstLetter, secondLetter, thirdLetter, fourthLetter, fifthLetter, sixthLetter, seventhLetter, eighthLetter]
self.databaseData = organizedData
self.make2darray.append(self.databaseData) //makes 2darray
} //closing fetch
} //closing else statement
print("printing", self.make2darray) // works ok
} //closing snapshot
print("printing outside snapshot", self.make2darray) //returns empty array
return self.make2darray //returns empty array
} //closing function
UPDATE: I've made the following changes to the code after reading and trying a few things. This should work as far as I understood what I read but I'm still getting an empty array.
func readFromDatabase(completion: #escaping ([[String]]) -> Void) {
let db = Firestore.firestore()
let dispatchGroup = DispatchGroup()
db.collection("Letters").getDocuments { (snapshot, error) in
if error != nil {
print(error?.localizedDescription ?? "error")
} else {
dispatchGroup.enter()
for document in (snapshot?.documents)! {
// let fetchedData = document.data() //gets all data from all documenters unorganized
let firstLetter = document.get("1") as! String
let secondLetter = document.get("2") as! String
let thirdLetter = document.get("3") as! String
let fourthLetter = document.get("4") as! String
let fifthLetter = document.get("5") as! String
let sixthLetter = document.get("6") as! String
let seventhLetter = document.get("7") as! String
let eighthLetter = document.get("8") as! String
let organizedData = [firstLetter, secondLetter, thirdLetter, fourthLetter, fifthLetter, sixthLetter, seventhLetter, eighthLetter]
self.databaseData = organizedData
self.make2darray.append(self.databaseData) //makes 2darray
} //closing fetch
dispatchGroup.leave()
} //closing else statement
//print("printing", self.make2darray) // works ok
dispatchGroup.notify(queue: .main){
completion(self.make2darray)
}
} //closing snapshot
} //closing function
you may be calling the print statement too early, try calling it after the function has run
override func viewdidload() {
super.viewDidLoad()
readFromDatabase()
print (make2darray)
}
Okay to anyone looking for an answer to this in the future. Here is how I was able to resolve the issue. I had to create an observer with a notification key.
The notification key was created as a variable right under the class declaration like this: let notificationKey = "#####". The hash would be a unique key like your website name or something.
In the viewDidLoad, I called createObserver() and called readFromDatabase() right after.
What is happening is that while the readFromDatabase() function is getting the data, the observer is waiting to hear a signal from the Notification Center. When the work is complete the Notification Center triggers the key we created that tells the observer the process is complete. That means now that "make2darray" has the complete data records pulled from the database and is ready for use. We must also remove the observer right after to not cause any issues later.
This code is crude I'm sure, but it works. I'm a new programmer, so use whatever way gets the same results for you. Good luck to you.
func createObserver(){
//listening to hear if the database call is complete
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.readFromDatabase), name: NSNotification.Name(rawValue: notificationKey), object: nil)
}
#objc func readFromDatabase() {
let db = Firestore.firestore()
db.collection("Letters").getDocuments { (snapshot, error) in
if error != nil {
print(error?.localizedDescription ?? "error")
} else {
for document in (snapshot?.documents)! {
// let fetchedData = document.data() //gets all data from all documenters unorganized
let firstLetter = document.get("1") as! String
let secondLetter = document.get("2") as! String
let thirdLetter = document.get("3") as! String
let fourthLetter = document.get("4") as! String
let fifthLetter = document.get("5") as! String
let sixthLetter = document.get("6") as! String
let seventhLetter = document.get("7") as! String
let eighthLetter = document.get("8") as! String
let organizedData = [firstLetter, secondLetter, thirdLetter, fourthLetter, fifthLetter, sixthLetter, seventhLetter, eighthLetter]
self.databaseData = organizedData
self.make2darray.append(self.databaseData) //makes 2darray
} //closing fetch
} //closing else statment
//Notifies the observer that the process is complete
NotificationCenter.default.post(name: NSNotification.Name(rawValue: self.notificationKey), object: nil)
//Removing the observer
NotificationCenter.default.removeObserver(self)
} //closing database call
//verifying all entries are present
//print("printing outside database call", self.make2darray.count)
} //closing function
I have a json string converted to string array like below:
let str = "{ \"dtResult\": [ { \"itmdtl_item_no\": \"AO406705959SE3\" }, { \"itmdtl_item_no\": \"AO406708959SE3\" } ] }"
let data = str.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject]
let result = json["dtResult"] as? [[String:Any]] ?? [ ]
let item = result[0] as! [String:Any]
print(item)
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
When i print out the result of item, i got the value like this:
["itmdtl_item_no": AO406705959SE3]
But i just want the string "AO406705959SE3", how can i do? Thanks.
First of all don't write
let result = json["dtResult"] as? [[String:Any]] ?? [ ]
If result is nil or empty the app will crash on result[0]
Instead write to check if the array exists and is not empty
if let result = json["dtResult"] as? [[String:Any]], !result.isEmpty {
let item = result[0] as! [String:Any]
// Now get the value for key "itmdtl_item_no"
if let itemNo = item["itmdtl_item_no"] as? String {
print(itemNo)
}
}
I have problem with JSON Array that I want to be display on textfield. JSON is taken from URL. This is JSON structure:
{
description = „This is short decripton”;
);
more-description = (
„this is first line”,
„this is second line”,
„third line”,
„etc”,
„etc”
);
one-more-description = (
„this is first line”,
„this is second line”,
„third line”,
„etc”,
„etc”
);
And this is my code:
import UIKit
class RecipeViewController: UIViewController {
#IBOutlet weak var descriptionTextField: UITextView!
#IBOutlet weak var more-descriptionTextField: UITextView!
#IBOutlet weak var one-more-descriptionTextField: UITextView!
override func viewDidLoad() {
super.viewDidLoad()
let urlAsString = "http://JSON-Address.com"
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
let jsonDescription = jsonResult["description"] as? String
print("result: \(jsonDescription)")
let jsonMoreDescrp: AnyObject? = jsonResult["more-description"] as? Array<AnyObject>
print("result: \(jsonMoreDescrp)")
let jsonOneMoreDescrp: AnyObject? = jsonResult["one-more-description"] as? Array<AnyObject>
print("result: \(jsonOneMoreDescrp)")
dispatch_async(dispatch_get_main_queue(),{
self.descriptionTextField.text = jsonDescription
self.more-descriptionTextField.text = jsonMoreDescrp as? String
self.one-more-descriptionTextField.text = jsonOneMoreDescrp as? String
});
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
The problem is with jsonMoreDescrp & jsonOneMoreDescrp. Althought I've changed it to String, after running Xcode the result is empty. jsonDescription of course works, but this is just simple string.
I know I'm doing something wrong with Array, but - can you tell me what?
jsonMoreDescrp & jsonOneMoreDescrp are array. so, you can call like this
self.more-descriptionTextField.text = jsonMoreDescrp[indexValue] as? String
self.one-more-descriptionTextField.text = jsonOneMoreDescrp[indexValue] as? String
Hope this will help you.
Try this way, if it not works comment me the error you come across.
self.more-descriptionTextField.text = jsonMoreDescrp[indexValue].stringValue
self.one-more-descriptionTextField.text = jsonOneMoreDescrp[indexValue].stringValue
indexValue is the key of the value you want in the json.
[Updated] As Sudhir noticed there is also error in code, try this to show comma separated strings:
dispatch_async(dispatch_get_main_queue(),{
self.descriptionTextField.text = jsonDescription
self.more-descriptionTextField.text = (jsonMoreDescrp as? [String])?.joinWithSeparator(",") ?? ""
self.one-more-descriptionTextField.text = (jsonOneMoreDescrp as? [String])?.joinWithSeparator(",") ?? ""
});
Validate JSON structure online before using http://jsonlint.com/
Valid JSON:
{
"description": "This is short decription",
"more-description": [
"this is first line",
"this is second line",
"third line",
"etc",
"etc"
],
"one-more-description": [
"this is first line",
"this is second line",
"third line",
"etc",
"etc"
]
}
try this code, it is much easy to use.
First of all parse all elements of map to the variables and than do what you want with knowing structure of just created variables.
let task = session.dataTask(with: url!) {
(data, response, error) in
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
if let desc = json["description"] as? String,
let moreDesc = json["more-description"] as? [String],
let oneMoreDesc = json["one-more-description"] as? [String] {
dispatch_async(dispatch_get_main_queue(),{
self.descriptionTextField.text = moreDesc
self.more-descriptionTextField.text = moreDesc.joinWithSeparator("/n")
self.one-more-descriptionTextField.text = oneMoreDesc.joinWithSeparator("/n")
});
}
} catch let error {
print (error)
}
}
task.resume()
I have not tested it, but it should work. Feel free to ask.