NSUserDefaults Seemingly not Creating Globally Accessible Variables - ios

I'm new to Swift and iOS in general, and would appreciate any help on this...I'm making an app with one file called ViewController.swift, which contains the ViewController class, and another file called BTService.swift, which contains the BTService class. I have a slider set up in the VC, and I want to take its value and use it in the BTService class. I asked a question about this previously (Swift Reference Variable from Specific Instance of Different Class) and someone suggested I use NSUserDefaults to accomplish this. This is my VC class with the slider value, positionSlider.value assigned to a key sliderValue
class ViewController: UIViewController {
#IBOutlet var positionLabel: UILabel!
#IBOutlet var positionSlider: UISlider!
#IBOutlet var connectionLabel: UILabel!
#IBAction func sliderValChanged(sender: UISlider) {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
println(positionSlider.value)
defaults.setFloat(Float(positionSlider.value), forKey: "sliderValue") // Write to NSUserDefaults
defaults.synchronize()
let currentValue: Float = defaults.floatForKey("sliderValue")
var currentValueString=NSString(format: "%.5f", currentValue)
sliderValue is stored as a float in NSUserDefaults. Now, from my understanding, I can then go to my BTService class and simply do this:
#IBAction func sliderValChanged(sender: UISlider) {
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
let currentValue: Float = defaults.floatForKey("sliderValue")
println(currentValue)
}
However, this isn't working. I don't get any syntax errors, but I also don't see any value printed to the console when I tweak the slider. Interesting note is that I do see an output of the value when I put a println statement in the VC, which shows that currentValue is being defined and is functioning like it should. Any help on this would be very much appreciated. Thanks so much!

#IBAction func sliderValChanged(sender: UISlider) {
NSUserDefaults.standardUserDefaults().setFloat(sender.value, forKey: "sliderValue")
let currentValue = NSUserDefaults.standardUserDefaults().floatForKey("sliderValue")
println(currentValue)
}

Related

Nil values passed after segue

I have an issue that has been driving me crazy, i had my values working inside my main VC which is named ViewController for both Age and Gender, i tested this with print statement and it works, this is below
#IBAction func uploadBtnAction(_ sender: UIButton) {
NetworkServices.instance.getGender(image: imageView1.image!) { (gender) in
self.Gender = gender
// print(self.Gender!) // This prints a value
}
NetworkServices.instance.getAge(image: imageView1.image!) { (age) in
self.Age = age
// print(self.Age!) //This prints a value
}
performSegue(withIdentifier: "updates", sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "updates" {
let destinationVC = segue.destination as! SecondViewController
destinationVC.userImage = image1
destinationVC.genderFromUser = Gender
destinationVC.ageFromUser = Age
}
}
but when i segue to another VC named SecondViewController which is below, i suddenly get an error "Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value", i have created variables to hold my values which will come from the first VC but this first VC does not pass the values after seque, please help, i have checked stackoverflow and tried all solutions to no avail, i though this should be the easiest part, funny enough, my image successfully passes over from the first VC to second during seque. Please advice.
import UIKit
import CLTypingLabel
class SecondViewController: UIViewController {
var userImage: UIImage?
var genderFromUser: String?
var ageFromUser: Int?
override func viewDidLoad() {
super.viewDidLoad()
imageView2.image = userImage
genderlabel.text = "Gender:"
ageLabel.text = "Age:"
print(ageFromUser!) // fatal error here
print(genderFromUser!) // fatal error here
print(userImage) // This is always successful
// ageUser.text =
// genderUser.text = userGender
// Do any additional setup after loading the view.
}
#IBOutlet weak var ageUser: UILabel!
#IBOutlet weak var genderUser: UILabel!
#IBOutlet weak var imageView2: UIImageView!
#IBOutlet weak var genderlabel: CLTypingLabel!
#IBOutlet weak var ageLabel: CLTypingLabel!
}
My goal is to set labels to the values passed over from the first VC at the second VC.
The problem is the order of operations. Your NetworkServices.instance.get... methods are asynchronous. I will use numbers to show you the order in which your code actually runs:
#IBAction func uploadBtnAction(_ sender: UIButton) {
NetworkServices.instance.getGender(image: imageView1.image!) { (gender) in
self.Gender = gender // 2 or 3
}
NetworkServices.instance.getAge(image: imageView1.image!) { (age) in
self.Age = age // 3 or 2
}
performSegue(withIdentifier: "updates", sender: self) // 1
}
So the segue is performed, and prepare is called, and the instance variables genderFromUser and ageFromUser are set, before you have set up your self.Gender and your self.Age. Therefore they are set to nil, because that's what they are at the time.
Later, of course, your asynchronous code comes along and does set self.Gender and self.Age, but it's too late; the train has left the station. SecondViewController already exists, and it has been configured with nil values.
Of course, since it does exist, you could now come along and set those instance properties again and get the SecondViewController to update its interface in response. Or you might work out some other solution. In general, this is a tricky problem — the problem of transitioning to another view controller at a time when its data is not ready yet and has to be fetched asynchronously.
[Also, you need to ask yourself, in fashioning a solution, what should happen if the data never arrives. What if the network chooses to go down at the exact moment the user taps the button? Welcome to the wild and wooly world of real life!]

Receiving 'Expected Declaration' when setting the image of a UIButton with <uibutton>.setImage [duplicate]

I'm trying to pass the boolean value of a UISwitch to another class using NSUserDefaults. For some reason, in the class that contains the switches, the if statements that are supposed to set the value to NSUserDefaults cannot read the switch declarations.
ViewController.swift
#IBOutlet var shrimpSwitch: UISwitch!
#IBOutlet var nutSwitch: UISwitch!
#IBOutlet var dairySwitch: UISwitch!
let switchState = NSUserDefaults.standardUserDefaults()
if shrimpSwitch.switch.on{
switchState.setBool(true, forKey: "shrimpSwitch")
}
else{
switchState.setBool(false, forKey: "shrimpSwitch")
}
if nutSwitch.on{
switchState.setBool(true, forKey: "nutSwitch")
}
else{
switchState.setBool(false, forKey: "nutSwitch")
}
if dairySwitch.on{
switchState.setBool(true, forKey: "dairySwitch")
}
else{
switchState.setBool(false, forKey: "dairySwitch")
}
In the first If statement(shrimpSwitch.on), it will say Expected Declaration. Am I declaring the switches all wrong? Any help would be appreciated. Thanks
The problem is that you need to put your code inside a method. All you need is to move it to viewDidLoad() or any other method.

My user defaults are not saving my slider value - Swift 3

I am creating a pomodoro timer app and I'm currently trying to implement user defaults in a slide up menu (Settings class), which is responsible for containing uisliders for adjusting the length of the timer. The user defaults looks pretty correct to me but whenever I close the app in the simulator and reopen it the value doesn't save? The slider and the label both go back to their default values. what am I doing wrong?
import Foundation
import UIKit
class Settings: UIView {
let defaults = UserDefaults.standard
let durationSlide = "durationSlide"
#IBOutlet weak var durationSlider: UISlider!
#IBOutlet weak var durationLabel: UILabel!
func viewDidLoad() {
if let durationSlide = defaults.value(forKey: durationSlide) {
durationSlider.value = durationSlide as! Float
durationSliderValueChanged(durationSlider)
}
}
#IBAction func durationSliderValueChanged(_ sender: UISlider) {
let currentValue = Int(sender.value)
durationLabel.text = "\(currentValue) minutes"
defaults.set(sender.value, forKey: durationSlide)
}
You should use the proper pair of methods to set and get your value in UserDefaults
You are saving the value with defaults.set(sender.value, forKey: durationSlide) which is saving a Float in UserDefaults.
But your attempt to read the value is using the wrong method. You are using value(forKey:) which isn't a method of UserDefaults. It's from the key-value coding extension to NSObject (which UserDefaults extends). Instead, use:
durationSlide = defaults.float(forKey: durationSlide)
This will give a value of 0 if there is no value in UserDefaults.

Saving an integer in core data

Overview
I need to save several TextFields into CoreData, but only the first one (Seen as pickerView below) saves and prints correctly. The others do not save correctly, for instance, when I try to save the integer ones, I get an error saying that they cannot take a String, which makes sense. I just cannot find a way to fix the integer-string issue. The other error occurs when I attempted to cast everything as a string ( mainly because I won't need to do any arithmetic on it, so it doesn't matter ), and it just gives me a breaking point in the saveButton function.
What I would like to know
What I ultimately need is the ability to save all of these TextFields into CoreData so that I can later retrieve them. I appreciate the help in advance. Thank you!
NOTE
I am including the entire ( or most of ) the ViewController.swift file so that you can see how I am declaring things and then how they are being called. The code in question is located in the saveButton action at the bottom of the code block.
CODE
#IBOutlet weak var locationOfMachine: UITextField!
#IBOutlet weak var engineHours: UITextField!
#IBOutlet weak var YOM: UITextField!
#IBOutlet weak var serialNo: UITextField!
#IBOutlet weak var modelName: UITextField!
#IBOutlet weak var pickerTextField: UITextField!
var pickOption = ["Wirtgen","Kleeman","Hamm","Vögele"]
override func viewDidLoad() {
super.viewDidLoad()
var pickerView = UIPickerView()
pickerView.delegate = self
pickerTextField.inputView = pickerView
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfComponentsInPickerView(pickerView: UIPickerView) -> Int {
return 1
}
#IBAction func saveButton(sender: AnyObject)
{
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext
var entity1 = NSEntityDescription.insertNewObjectForEntityForName("UsedInfo", inManagedObjectContext:context) as NSManagedObject
entity1.setValue(pickerTextField.text, forKey: "product")
entity1.setValue(modelName.text, forKey:"modelName")
entity1.setValue(serialNo.text, forKey:"serialNo")
entity1.setValue(Int(YOM.text!), forKey:"yom")
entity1.setValue(engineHours.text, forKey:"engineHours")
entity1.setValue(locationOfMachine.text, forKey:"location")
print(entity1.valueForKey("product"))
print(entity1.valueForKey("modelName"))
print(entity1.valueForKey("serialNo"))
print(entity1.valueForKey("yom"))
print(entity1.valueForKey("engineHours"))
do {
try context.save()
}
catch {
print("error")
}
}
EDIT
Upon trying to save everything as just a string, since i only need to retrieve it, I run into this issue:
entity1.setValue(pickerTextField.text, forKey: "product")
entity1.setValue(modelName.text, forKey:"modelName")
entity1.setValue(serialNo.text, forKey:"serialNo") <-Thread1:Breakpoint1.1
entity1.setValue(YOM.text, forKey:"yom")
entity1.setValue(engineHours.text, forKey:"engineHours")
entity1.setValue(locationOfMachine.text, forKey:"location")
print(entity1.valueForKey("product"))
print(entity1.valueForKey("modelName"))
print(entity1.valueForKey("serialNo"))
print(entity1.valueForKey("yom"))
print(entity1.valueForKey("engineHours"))
I also get "(lldb)" in the debugger window.
I'll just show you how to get int from string. Use it accordingly:
var aString = "0000" // var aString = textField.text!
var numFromString = Int(aString)
You can assign the text field to aString and convert it to Int like i showed you.
For things that don't need arithmetic, define them as strings in Core Data. For other numbers, it should work to do as you have with Int(YOM.text!).
However, I suggest that you create a managed object subclass for "UsedInfo" so that you can work directly with its properties instead of using setValue:forKey:. The benefit of a subclass is that it will show you data types explicitly.
Validate all textfields before trying to store,set the appropriate keyboard for each textfield and provide the valid character set for each textfield.
For Example:
YOM text field : Use Keyboard with only integers.
Valid character set are 0 to 9
And validation for min and max if applicable.
If any of the validation criteria fails ,throw an alert to input valid data.
I guess this solves your issue.

Initialize object in parse

I am trying to implement a like feature. I have a label and a button. If the button is pressed it should add one vote to Parse and the label would retrieve the number of votes. However it does not work with my code. It looks like I need to initialize parseObject but I don't know how to. my code for the cell is below. Thank you. I have been stuck for this problem for days.
import UIKit
import ParseUI
import Parse
var parseObject:PFObject?
var votes = [PFObject]()
class NewCollectionViewCell: UICollectionViewCell {
var parseObject = PFObject(className: "Posts")
#IBOutlet weak var postsImageView: PFImageView!
#IBOutlet weak var postsLabel: UILabel!
#IBOutlet weak var votesLabel:UILabel?
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
postsLabel.textAlignment = NSTextAlignment.Center
}
#IBAction func vote(sender: AnyObject) {
if (parseObject != nil) {
if let votes = parseObject!.objectForKey("votes") as? Int {
parseObject!.setObject(votes + 1, forKey: "votes")
parseObject!.saveInBackgroundWithTarget(nil, selector: nil)
votesLabel?.text = "\(votes + 1) votes"
}
else {
parseObject!.setObject(1, forKey: "votes")
parseObject!.saveInBackgroundWithTarget(nil, selector: nil)
votesLabel?.text = "1 votes"
}
}
}
}
First of all, you have implement you target method. I am not sure whether it will save the object or not when you pass in nil. For making it consistent, you better include target and selector because it will also tell you whether the object is saved successfully. You can also go to the Parse dashboard to check whether it's saved successfully or not.
And yes, you have not instantiate your object. Otherwise, your if-else statement just got executed and did nothing. If you try to debug it, the parseObject is actually nil. In Parse, there are several ways to do and docs provides you everything: https://parse.com/docs/ios/api/Classes/PFObject.html#//api/name/objectWithClassName:

Resources