I am trying to save an array and whenever I reload the app the array is empty.
I have tried userDefaults and core Data. Each time I build an array, it starts empty when I load the app. I'm unsure of where to save the array and if adding to the array through an IBAction is causing the problem
firstArray: [Double] = []
#IBAction func userSubmit(_ sender: Any) {
oneArray()
func oneArray(){
if selected == "Breakfast" {
if let firstArrays = Double(glucoseReading.text!) {
//append new element to Array
firstArray.append(firstArrays)
print(breakfastSugarsArray)
}
}
}
I want to be able to save the array on app-relaunches and not have the array start empty each time the app is loaded. My problem may be I don't know where to save the array, or how to do it.
I want to be able to save the array on app-relaunches and not have the array start empty each time the app is loaded.
Every time the userSubmit(...) is going to be triggered it will check the value of selected as in your example, check if there is some Double data in glucosReading.text and then retrieve your array saved in UserDefault and update it with the new values.
#IBAction func userSubmit(_ sender: Any) {
if selected == "Breakfast" {
if let glucoseData = Double(glucoseReading.text!) {
// retrieve from UserDefault if none create an empty array
let glucoseListData = UserDefaults.standard.array(forKey: "glucoseListData") as? [Double] ?? [Double]()
// store in UserDefault
glucoseListData.append(glucoseData)
UserDefaults.standard.set(glucoseListData, forKey: "glucoseListData")
// check print outputs in Xcode logs
println("Glucose data list: \(glucoseListData)")
}
}
}
Related
I am getting values from a firebase real-time database. I want to store these values into an array and display them in a UITableView. Here is what is happening:
I have defined the Array before my viewDidLoad() function as the following:
var taskTitles: [Task] = []
In my viewDidLoad() function I am calling another function to generate the array:
override func viewDidLoad() {
super.viewDidLoad()
//Setting the Title for the nav bar
title = "To Do List"
configureNavigationItems()
taskTitles = createArray() // creating the array of tasks in db
tableView.delegate = self
tableView.dataSource = self
}
In this function, I am passing information to my classes. Task and TaskCell. They are simply just handling the Title of the task.
func createArray() -> [Task] {
taskRef = Database.database().reference(withPath: "Tasks")
//getting values from db, storing them in an array.
refHandle = taskRef?.observe(DataEventType.value, with: { snapshot in
for taskSnapshot in snapshot.children {
let nodeA = taskSnapshot as! DataSnapshot
let keyA = nodeA.key
let theTask = Task(title: String(keyA))
self.taskTitles.append(theTask)
print("In the FOR Loop --> ", self.taskTitles)
}
print("outside of FOR Loop --> ", self.taskTitles)
})
print("outside of observe func --> ", taskTitles)
return taskTitles
}
}
However, it doesn't seem to save my items into the array. I did some debugging to figure where things were going wrong. Hopefully, the picture below can clarify:
Any idea what the issue is?
Your call to taskRef?.observe is asynchronous. That is why you see "outside of observe func --> []" printed before the other lines.
What is happening is that your createArray() function calls observe and then returns taskTitles which is still empty. Then your view finishes loading and shows (presumably) an empty table. After this the observe function calls your closure with the snapshot and you update taskTitles, but at this point tableView is already on screen and empty and you would have to take further actions to reload it (like, for example, calling reloadData() on it).
It is perhaps also worth mentioning that you are modifying your property in that function, then returning it, and assigning it to itself. This is perhaps redundant.
I have an empty global array. The only simple thing I want to do is add an element to this array. It seems in swift this seemingly simple task is proving to be difficult. I am just left with an empty array and nothing is appending to my global array.
I can see that it prints out values in the for loop. So the values are actually there.
This is some stuff I have declared globally (Yes, I know global variables are bad but I will sort that out later):
struct HouseDetails: Decodable {
let median_price: String
let sale_year: String
let transaction_count: String
let type: String
}
var hsArray: [HouseDetails] = []
and in the viewDidLoad() function I have the data which I am storing in local variable "houses". When I loop through the array it prints median_price, showing that the values are there.
However when I do hsArray.append(h) it seems to do nothing.
let jsonUrlString = "https://data.melbourne.vic.gov.au/resource/i8px-csib.json"
guard let url = URL(string: jsonUrlString)
else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let houses = try JSONDecoder().decode([HouseDetails].self, from: data)
for h in houses {
hsArray.append(h)
print(h.median_price)
}
}
catch let jsonErr {
print("Error with json serialization", jsonErr)
}
}.resume()
Thank you for any help. In other languages I am used to being able to append an element to the end of an existing array, so I am sure it is just a small error.
Firstly, why don't you simply do
hsArray.append(contentsOf: houses)
instead of all that for loop
for h in houses {
hsArray.append(h)
print(h.median_price)
}
The issue might be the time at which you are using hsArray. See if the response is received after you use hsArray.
I have this closure which I use to populate my array and dictionary. However, when I try to use it outside the function, it's empty. Here's my code:
var pages: [FBPages] = []
override func viewDidLoad() {
super.viewDidLoad()
Facebook.getUserInfo(greetingLabel: greetingLabel, profileImage: profilePic)
pages = []
let params = ["fields": "about,name,created_time,picture", "limit": "3"]
Facebook.getUserPagesLikes(params: params, handler: { (userData) in
guard let pagesArrays = userData["data"] as? Array<Any> else {return}
for dict in pagesArrays {
let fbPages = FBPages()
let pagesData = dict as! NSDictionary
fbPages.about = pagesData["about"] as! String
fbPages.name = pagesData["name"] as! String
self.pages.append(fbPages)
}
print("pages array: \(pagesArrays)")
print("pages : \(self.pages.count)")
}) { (error) in
print("cannot get FB pages: \(String(describing: error))")
}
//I got 0 here
print("fb array: \(pages.count)")
}
I tried to print array count outside closure but I only got 0 as a result. How can I solve this problem. Thanks in advance.
You will need to understand that the time you are printing array. It is not yet available. getUserPagesLikes is an async call. Which is taking a completion handler. Once the response comes back, you will have values in your array.
You can understand the sequence like you ask getUserPagesLikes to fetch the user likes for you and you don't wait for user likes there. You move ahead, and whatever code you write after completion block gets called. Which in your case turns out to be printing array.
Once the getUserPagesLikes gets back the data for you its completion block gets called. At this time you assign the array values. The values in array would be available after this only. So if you want to reload data in table view do it in completion handler. Also make sure that you do it on main thread.
For a detail information on completion handler and their sequence of call you can go through here.
Im new to Swift and Im having a hard time understanding why this is not working - I've tried many different combinations of this through examples on stackoverflow and my variable "collections" still comes out empty (see last line) so I'm guessing I'm missing a small (but important) detail. Appreciate any help!
class CollectionsViewController: UITableViewController {
var collections = NSMutableArray()
override func viewDidLoad() {
super.viewDidLoad()
SDK.sharedInstance()
.getAuthenticatedUserBoards(withFields: ["id", "name","url","description","image"],
success: { data in
guard let myData = data?.parsedJSONDictionary["data"] as? [[String: Any]]
else {
return
}
for item in myData {
self.collections.add(item)
}
}, andFailure: nil)
print("collections.....\(collections)")
//Output: collections.....()
}
The loading of the data is asynchronous. Since your goal to load your table view after the data loads, you need to call reloadData on your table view at the end of the success block. But UI calls must be made on the main queue so you should use DispatchQueue to do this.
Here is what you need:
override func viewDidLoad() {
super.viewDidLoad()
SDK.sharedInstance()
.getAuthenticatedUserBoards(withFields: ["id", "name","url","description","image"],
success: { data in
guard let myData = data?.parsedJSONDictionary["data"] as? [[String: Any]]
else {
return
}
for item in myData {
self.collections.add(item)
}
print("collections.....\(collections)")
DispatchQueue.main.async {
tableView.reloadData()
}
}, andFailure: nil)
}
Since you are currently updating self.collections in the background, there is a small chance your UI on the main queue will see a partially up-to-date set of data. So I would recommend one further change:
override func viewDidLoad() {
super.viewDidLoad()
SDK.sharedInstance()
.getAuthenticatedUserBoards(withFields: ["id", "name","url","description","image"],
success: { data in
guard let myData = data?.parsedJSONDictionary["data"] as? [[String: Any]]
else {
return
}
var list = NSMutableArray()
for item in myData {
list.add(item)
}
DispatchQueue.main.async {
self.collections = list
print("collections.....\(collections)")
tableView.reloadData()
}
}, andFailure: nil)
}
This ensure the main collections property is only updated on the main thread.
I also suggest you use a Swift array of a specific type instead of using an NSMutableArray.
I'm assuming getAuthenticatedUserBoards is making some sort of asynchronous call to an API. Since we're not sure how long that call is going to take, the task is placed on a background thread while the rest of the app's tasks continue to run on the main thread. Your print statement is one of those tasks that will run while the API call is happening in the background. In essence, the print statement executes before anything inside of the getAuthenticatedUserBoards is completed.
The list of collections should be printed directly after the for loop and before the end of the closure. You can verify the order in which the different tasks are completed by placing print statements at various different points within your function - you should be able to see that your current print statement will print before any other statements that you place inside of the closure.
When I try to store arrays from Parse into a local array I can only access it within the findObjectsInBackgroundWithBlock {...}. When I print it outside of that block, it shows []...
Code :
var qArray : [[Int]] = []
override func viewDidLoad() {
super.viewDidLoad()
let query = PFQuery(className: "Trivia")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) in
if objects != nil {
if let objects = objects {
for object in objects {
self.qArray.append(object["mainPattern"] as! [Int])
}
print(self.qArray) // Prints a multi dimension array
}
}
if error != nil {
print(error)
}
}
print(self.qArray) // prints []
}
It's most likely because the array hasn't been populated yet because it's running in the background. You can try using dispatch_group to circumvent this issue.
I think you're misunderstanding what findInBackground means. It means the code after the callback continues to execute, so it calls query.findInBackground.... and then it continues with the next line, which is print(self.qArray). At some point later on, it hears back from the database and it executes all the code inside the Callback, which is when the array finally gets populated.