Append an array and store in UserDefaults (Swift) - arrays

My project has an alert button with a UITextField that allows me to input a String that is then appended to an array that I have declared globally. This addition allows another UITextField to show the addition in its drop down menu. However, the changes will only save for the time the app remains open, and will not save when I try to configure UserDefaults.
I've read through what looked like similar S.O. postings, but I am unable to get any of the solutions to work with my issue.
(This is declared globally.)
let defaults = UserDefaults.standard
(This is also global.)
var needIndicatorArray: [String] = ["SNAP", "EBT", "FVRX"]
(This is the code I'm using to append the above mentioned array. This code will append the array, but will not save the addition after the app is closed and re-opened.)
#IBAction func addNeedIndicator(_ sender: UIBarButtonItem) {
var textField = UITextField()
let alert = UIAlertController(title: "Add Need Indicator", message: "", preferredStyle: .alert)
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
//This should append the global array once the user hits the add item on the UIAlert
self.needIndicatorArray.append(textField.text!)
}
alert.addTextField { (alertTextField) in
alertTextField.placeholder = "Create new item"
textField = alertTextField
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
}

I am not seeing where you actually write to the defaults. You should have a line with something like:
defaults.set(needIndicatorArray, forKey: "someKey")
But also, you never check to see what is in defaults. You need to load it with something like:
needIndicatorArray = defaults.object(forKey: "someKey") as? [String] ?? ["SNAP", "EBT", "FVRX"]
As an aside, all global variables are lazy, and you should not depend on them. You are better off declaring them locally or as a static in some class or struct. BTW, when I say 'lazy', I am referring to a type of variable, not commenting on your coding style. A lazy variable can lose the reference under certain conditions.

You need to save to User Defaults and then read back the array when needed.
I have just included the relevant section below, when you should save to User Defaults:
let action = UIAlertAction(title: "Add Item", style: .default) { (action) in
// This should append the global array once the user hits the add item on the UIAlert
self.needIndicatorArray.append(textField.text!)
// You need to save to User Defaults
defaults.set(needIndicatorArray, forKey: "yourKey")
}
When you need to retrieve the array, use this:
let array = defaults.object(forKey: "yourKey") as? [String] ?? [String]()

Related

Swift - Update Value of a specific item in a nested dictionary

I have a tableview and each cells are meant to be linked to an array inside a dictionary.
var buttonAction : [String: [ButtonAction]] = [:]
below is the struct of the buttonAction
struct ButtonAction: Codable {
var action: String
var array_linked_of_buttons: [[String:String]]
init(action: String, array_linked_of_buttons: [[String:String]]) {
self.action = action
self.array_linked_of_buttons = array_linked_of_buttons
}
}
It gets a bit complicated to explain the whole code but when I connect two buttons together, I can get the data for the variable "singleAction" which then can be added to the button action dictionary "array_linked_of_buttons".
let singleAction = [linkedButtonUUID: connectorMarker.UUIDpic.uuidString, linkedButtonCategory: "", linkedButtonName: connectorMarker.name]
let mainMarkerAction = mainMarker.buttonAction["button actions array"]!
for existingMarker in mainMarkerAction {
actionArray.append(existingMarker)
}
var actionSub = actionArray[indexRowTag].array_linked_of_buttons
if let addAction = actionSub.filter({$0[linkedButtonUUID] == connectorMarker.UUIDpic.uuidString}).first {
print("MARKER Exists", addAction)
} else {
actionSub.append(singleAction)
print("UPDATED MARKER", actionSub)
}
let action = ButtonAction(action: actionArray[indexRowTag].action, array_linked_of_buttons: actionSub)
//ISSUE--?? mainMarker.buttonAction.updateValue(VALUE forKey: "button actions array")
saveData()
I can workout which item of the dictionary needs to be edited but how do I update that specific value? I am really confused has it just creates a new item but I want to update a previous one.
So for example, I want to append the "array_linked_buttons" of the item 0 and have 4 items instead of 3.
This is how my dictionary looks like if that helps too
I have searched other questioned but I still work it out.
Any help pointing me in the right direction is much appreciated!
Copying instead of changing value happened because you're using struct for ButtonAction which has value semantics and swift create copy on assigning to any variable.
You need to use class instead. Classes have reference semantics, so it won't create new instance on assigning to variable so you'll be able to update property of needed instance.
I worked out i was updating the main dictionary array and not the nested array.
The code below is what I used in the end.
let singleAction = [linkedButtonUUID: connectorMarker.UUIDpic.uuidString, linkedButtonCategory: "", linkedButtonName: connectorMarker.name]
var mainMarkerAction = mainMarker.buttonAction["button actions array"]!
for existingMarker in mainMarkerAction {
actionArray.append(existingMarker)
}
var actionSub = actionArray[indexRowTag].array_linked_of_buttons
if let addAction = actionSub.filter({$0[linkedButtonUUID] == connectorMarker.UUIDpic.uuidString}).first {
print("MARKER HERE", addAction)
} else {
actionArray[indexRowTag].array_linked_of_buttons.append(singleAction)
}
mainMarkerAction[indexRowTag] = actionArray[indexRowTag]

saving array data as user default and loading array in table view

I created an array and I'm populating the data (type Double) onto a table view. But when the app closes, all the data goes away! Im having trouble saving the array as a User Default so that when the user closes the app and opens it again, all of the info is still populated and available to see.
I set up an array called moneySpentArray, and this array holds all of a users entered transactions. After user clicks "submit", the IB action takes the input and sends it thru a function called addToMoneySpentArray
#IBAction func submitNewInfo(_ sender: UIButton) {
AddSubtractMoneyController.addToMoneySpentArray(amountISpent: Double(outputLabel.text!)!)
}
Below is the addToMoneySpentArray function that I send the data too, which then appends that amount into the 'moneySpentArray'.
class func addToMoneySpentArray( amountISpent: Double) {
moneySpentArray.append(amountISpent)
print(moneySpentArray)
}
Now in my SpendingHistoryVC- when the View loads, I use the array.reduce to add up the sum of the values in the array and display it in a text label called "youSpentThisMuch"- and Below this label will be the table view, which holds all the individual indexed amounts from the array also. Im confused on where and when to implement user defaults so that when a user enters a new amount through the IBAction above, it automatically stores and updates the array as a new user default. So even if the app is closed, their array history is still available to see.
override func viewDidLoad() {
super.viewDidLoad()
let arraySum: Double = AddSubtractMoneyController.moneySpentArray.reduce(0.0, +)
youSpentLabel.text = "\(arraySum)"
}
In addToMoneySpentArray save the data:
class func addToMoneySpentArray( amountISpent: Double) {
moneySpentArray.append(amountISpent)
UserDefaults.standard.set(moneySpentArray, forKey: "moneySpent")
print(moneySpentArray)
}
and in viewDidLoad load it
override func viewDidLoad() {
super.viewDidLoad()
let moneySpentArray = UserDefaults.standard.array(forKey: "moneySpent") as? [Double] ?? [Double]()
AddSubtractMoneyController.moneySpentArray = moneySpentArray
let arraySum = moneySpentArray.reduce(0.0, +)
youSpentLabel.text = "\(arraySum)"
}

Swift PHP Post and UserDefaults

I am working on an App which will show a form (name, last name, phone, etc.) and that information is sent to a PHP that I have on my domain. After registration, I want to show the info to the user (via UserDefauls so the info is stored).
What I don't know is how to use this property and how to change the labels on the view where I will show the information. For additional information, the information is stored in an array "str[value]"
I did something like this...
let UDName = UserDefaults()
var dataName: String?
#IBAction func sendInfo(_ sender: Any) {
//PHP Post process
//This is the String
self.dataName = str3[0]
//The UserDefaults... value?
self.UDName.set(self.dataName, forKey: "name")
}
Now, the Button that retrieves/shows the information:
#IBAction func showInfo(_ sender: Any) {
if let name = UDName.string(forKey: "name") {
//This is the label from the form in the view
labelNombre.text = name
}
}
When I run the project, fill the textfields and save, everything is ok (prints the array with the info) but when I click the showInfo button, I get a Nil error in the code:
labelName.text = name
I appreciate your help :D

How to pass array of images to detailView Swift?

Well I have been making a test app to continue my swift learning, today I came across a problem.
Basically I have a tableview and a detailview. For my tableview file I have some data that I am currently passing to the detailview, like the name that goes on the navigation bar, one image and some text, this data is stored in arrays on my tableview file, I use "prepareforsegue" to pass this information:
var names = ["name1","name2","name3"]
detailViewController.detailName = names[indexPath.row]
Then in my detailViewController I have variables set for that:
var detailName: String?
Then I use this for stuff, example: naming my navigation bar or setting an image in detailView:
navigationItem.title = detailName!
Now, what I dont get how to do is pass a whole array of information to a variable in my detailViewController. What I want to do is pass an array of images to use it on my detailView. I want to be able to iterate through the array of images with a button, I know how to set that up but I just need to know how to pass the array, right now I am just passing one of the values(one name, one image etc...)
Thanks for the help in advance.
It's not so different from what you have done.
Just add field for images in detailViewController, and then pass images using it. Images could be represented in [UIImage]. However, [String] can be also used for local filenames, and [NSURL] can be used for remote image urls.
code:
In DetailViewController:
var images: [UIImage]? // or var images: [UIImage] = []
In prepareForSegue:
detailViewController.images = YourImages
You seem to be asking for one of two things:
A UIImage array, which you can declare using var imageArray : [UIImage] = [] and then append whatever images you want to pass.
Alternatively, you can pass in a [AnyObject] array and cast its elements to a UIImage or String by doing
var objectArray : [AnyObject] = []
objectArray.append("test")
objectArray.append(UIImage(named: "test.png")!)
if let s = objectArray[0] as? String {
// Do something with the string
}
if let i = objectArray[1] as? UIImage {
// Do something with the image
}
if let s = objectArray[1] as? String {
// The above cast fails, this code block won't be executed
} else {
// ... but this one will
}
in the table view controller file:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get reference to the destination view controller
var detailVC = segue.destinationViewController as! DetailViewController
var detailImages: Array<UIImage> = []
detailImages.append(UIImage(named: "pup.png")!)
detailImages.append(UIImage(named: "cutepuppy.png")!)
detailVC.detailImages = detailImages;
}
and in the detail file:
var detailImages: Array<UIImage>?

SWIFT - helper class callback

In order to manage/shorten my code a little, I decided to try to create a helper class to manage all alerts.
The idea is that the helper class will create the alert. When the user presses the button, it will then send a callback to inform the code on the viewController which button is pressed. Then based on that I can fire different bits of code.
Currently the code I have manages to pop up the alert correctly, but I cannot get it to send the callback when the button is pressed.
here is my alert manager I created so far(still basic for simplicity):
var alertMgr: alertManager = alertManager()
class alertManager: NSObject {
func showAlert(titleText:String?, confirmText:String, cancelText:String, msgText:String, showCancel:Bool, showConfirm:Bool, style:UIAlertControllerStyle, viewController:UIViewController) -> Int? {
let alertController = UIAlertController(title: titleText, message: msgText, preferredStyle: style)
var myInt:Int!
let actionLeft = UIAlertAction(title:cancelText, style: .Cancel) { action in
println("0") //works when user presses cancel button
myInt = 0
}
let actionRight = UIAlertAction(title:confirmText, style: .Default) { action in
println("1") //works when user presses confirm button
myInt = 1
}
alertController.addAction(actionLeft)
alertController.addAction(actionRight)
viewController.presentViewController(alertController, animated: true, completion: nil)
return myInt //does not return when button is pressed???
}
}
In my code when I want to show an alert I use the following code:
let titleTxt:String = "title txt goes here."
let confirmTxt:String = "confirm"
let cancelTxt:String = "cancel"
let msgTxt:String = "some msg goes here."
let showCancel:Bool = true
let showConfirm:Bool = true
let style:UIAlertControllerStyle = .ActionSheet
let alert = alertMgr.showAlert(titleTxt, confirmText: confirmTxt, cancelText: cancelTxt, msgText: msgTxt, showCancel: showCancel, showConfirm: showConfirm, style:style, viewController: self)
switch alert as Int! {
case nil:
println("nil value")
case 0:
println("cancel pressed") //never gets fired. when button is pressed
case 1:
println("confirm pressed") //never gets fired. when button is pressed
default:
break
}
The main problem is I cannot seem to get the call back to send the "myInt" value on button press. I have searched through pretty much all questions on here with [UIAlertController] and have not been able to find any hints that would push me in the right direction.
Any help would be great.
showAlert returns the value of myInt before any of the given blocks gets a chance to be executed, the solution is to pass complete action-blocks as parameters of showAlert. In order to shorten the signature you can prepare a special type with text, block and flag values and make the function to process arrays of the objects of this type.

Resources