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.
Related
Using UserDefaults to store two user input fields to be used throughout various VCs. UILabels show the current settings, while UITextFields accept updated input. I can set, store, display and update the two fields, but updates have to be in tandem. I cannot figure out the proper method to update only one of the two fields. As an example, update the email only, without touching "evernote" in my case. Without the work around below, and when only inputting one of two fields, the second non-input field is overridden as blank.
As a work around, I include the saved data as the initial input for the two UITextFields. Code works, just does not seem to be optimal. Ideally the two UITextFields would not have initial values. I have mucked around with != null statements in the button function, but to no avail.
Any advice would be greatly appreciated, and thank you in advance.
Class to share userData input among VCs:
class UDM {
static let shared = UDM()
let userData = UserDefaults(suiteName: "xxxx.saved.data")!
}
Outlets and Actions:
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var evernoteLabel: UILabel!
#IBOutlet weak var enterEmail: UITextField?
#IBOutlet weak var enterEvernote: UITextField?
#IBAction func saveButton(_ sender: UIButton) {
UDM.shared.userData.set(enterEmail?.text, forKey: "email")
UDM.shared.userData.set(enterEvernote?.text, forKey: "evernote")
}
viewDidLoad UI:
let emailObject = UDM.shared.userData.object(forKey: "email")
if let email = emailObject as? String {
enterEmail?.text = email
emailLabel.text = email
}
let evernoteObject = UDM.shared.userData.object(forKey: "evernote")
if let evernote = evernoteObject as? String {
enterEvernote?.text = evernote
evernoteLabel.text = evernote
}
Posting in case someone else is as dumb as I am. In the if/then statements for the UITextFields, I looked at enterEmail or enterEvernote as fields. Once I realized you need to look at the .text in the fields, it all fell into place. Updated action is pretty simple then:
#IBAction func saveButton(_ sender: UIButton) {
if enterEmail?.text != "" {
UDM.shared.userData.set(enterEmail?.text, forKey: "email")
}
if enterEvernote?.text != "" {
UDM.shared.userData.set(enterEvernote?.text, forKey: "evernote")
}
}
I have created a dummy IOS Application to explain my questions well. Let me share it with all details:
There are 2 Pages in this dummy IOS Application: LoginPageViewController.swift and HomepageViewController.swift
Storyboard id values are: LoginPage, Homepage.
There is login button in Login page.
There are 3 labels in Homepage.
App starts with Login page.
And i have a class file: UserDetail.swift
And there is one segue from login page to home page. Segue id is: LoginPage2Homepage
UserDetail.swift file
import Foundation
class UserDetail {
var accountIsDeleted = false
var userGUID : String?
var userAge: Int?
}
LoginPageViewController.swift file
import UIKit
class LoginPageViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginButtonPressed(_ sender: UIButton) {
var oUserDetail = UserDetail()
oUserDetail.accountIsDeleted = true
oUserDetail.userAge = 38
oUserDetail.userName = "Dirk Kuyt"
UserDefaults.standard.set(oUserDetail, forKey: "UserCredentialUserDefaults")
UserDefaults.standard.synchronize()
performSegue(withIdentifier: "LoginPage2Homepage", sender: nil)
}
}
HomepageViewController.swift file
import UIKit
class HomepageViewController: UIViewController {
var result_userGUID = ""
var result_userAge = 0
var result_isDeleted = false
#IBOutlet weak var labelUserGuidOutlet: UILabel!
#IBOutlet weak var labelAgeOutlet: UILabel!
#IBOutlet weak var labelAccountIsDeletedOutlet: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
self.setVariablesFromUserDefault()
labelUserGuidOutlet.text = result_userGUID
labelAgeOutlet.text = String(result_userAge)
labelAccountIsDeletedOutlet.text = String(result_isDeleted)
}
func setVariablesFromUserDefault()
{
if UserDefaults.standard.object(forKey: "UserCredentialUserDefaults") != nil
{
// I need a help in this scope
// I have checked already: My UserDefault exists or not.
// I need to check type of the value in UserDefault if UserDefault is exists. I need to show print if type of the value in UserDefault is not belongs to my custom class.
// And then i need to cast UserDefault to reach my custom class's properties: userGUID, userAge, isDeleted
}
else
{
print("there is no userDefault which is named UserCredentialUserDefaults")
}
}
}
My purposes:
I would like to store my custom class sample(oUserDetail) in UserDefaults in LoginPageViewController with login button click action.
I would like to check below in home page as a first task: My UserDefault exists or not ( I did it already)
I would like to check this in home page as a second task: if my UserDefault exists. And then check type of the UserDefault value. Is it created with my custom class? If it is not. print("value of userdefault is not created with your class")
Third task: If UserDefault is created with my custom class. And then parse that value. Set these 3 variables: result_userGUID, result_userAge, result_isDeleted to show them in labels.
I get an error after I click the login button in Login Page. Can't I store my custom class in UserDefaults? I need to be able to store because I see this detail while I am writing it:
UserDefaults.standart.set(value: Any?, forKey: String)
My custom class is in Any scope above. Isn't it?
You can't store a class instance without conforming to NSCoding / Codable protocols
class UserDetail : Codable {
var accountIsDeleted:Bool? // you can remove this as it's useless if the you read a nil content from user defaults that means no current account
var userGUID : String?
var userAge: Int?
}
store
do {
let res = try JSONEncoder().encode(yourClassInstance)
UserDefaults.standard.set(value:res,forKey: "somekey")
}
catch { print(error) }
retrieve
do {
if let data = UserDefaults.standard.data(forKey:"somekey") {
let res = try JSONDecoder().decode(UserDetail.self,from:data)
} else {
print("No account")
}
}
catch { print(error) }
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.
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:
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)
}