I have a survey form that appear only if the user still didn't answer survey every time the user launch the app. But even if I already answered the survey still its appearing. Here's how I'm doing it
//Get if questionaire is done
let defaults = UserDefaults.standard
if let questionaire_done = defaults.string(forKey: "questionaire_done") {
print(questionaire_done) // Some String Value
}else {
performSegue(withIdentifier: "questionaireSegue", sender: nil)
}
UserDefaults.standard.set("yes", forKey: "questionnaire_done") //set the questionnaire as done
if let questionaire_done = defaults.string(forKey: "questionaire_done")
has a different key to
UserDefaults.standard.set("yes", forKey: "questionnaire_done")
Spell the keys the same, or better still, use a constant string for the key.
Store the value in before segue (navigation)
//Get if questionaire is done
let defaults = UserDefaults.standard
if let questionaire_done = defaults.string(forKey: "questionnaire_done") {
print(questionaire_done) // Some String Value
}else {
defaults.set("yes", forKey: "questionnaire_done") //set the questionnaire as done
performSegue(withIdentifier: "questionaireSegue", sender: nil)
}
If you want to validate your condition then you like
let defaults = UserDefaults.standard
if let questionaire_done = defaults.string(forKey: "questionnaire_done"), questionaire_done == "yes" {
print(questionaire_done) // Some String Value
}else {
defaults.set("yes", forKey: "questionnaire_done") //set the questionnaire as done
performSegue(withIdentifier: "questionaireSegue", sender: nil)
}
Yes, the key value name should be always same but one more thing - you should write this after any "UserDefaults" operation - either read or write.
UserDefaults.standard.synchronize()
Example -
UserDefaults.standard.set("yes", forKey: "questionnaire_done")
UserDefaults.standard.synchronize()
Related
I want to save whichever indexpath I last clicked in the menu. He's recording indexpath right now. But if I choose indexpath, he's recording it. If I don't choose, it doesn't. How can I keep the previous record even if I don't select item from the menu?
let save11 = UserDefaults.standard.integer(forKey: "indexPath")
let yenindex = save11
self.menuView.setSelected(index: yenindex)
let userDefaults = UserDefaults.standard
userDefaults.removeObject(forKey: "indexPath")
menuView.didSelectItemAtIndexHandler = {(indexPath: Int) -> Void in
var placesdeger: String = ""
UserDefaults.standard.setValue(indexPath, forKey: "indexPath")
if indexPath == 0 {
let userDefaults = UserDefaults.standard
userDefaults.removeObject(forKey: "indexPath")
UserDefaults.standard.setValue(indexPath, forKey: "indexPath")
placesdeger = "Yatak Odası"
}
if indexPath == 1 {
let userDefaults = UserDefaults.standard
userDefaults.removeObject(forKey: "indexPath")
UserDefaults.standard.setValue(indexPath, forKey: "indexPath")
placesdeger = "Oturma Odası"
self.refreshData()
}
when ever you click on menu remove previous Userdefault value and set new user default value every time.
Put this code and get new index every time and if not selected then old value you will get in Userdefaults
let userDefaults = UserDefaults.standard
userDefaults.removeObject(forKey: "index")
userDefaults.set(i, forKey: "index")
EDIT1
USE THIS IN SOMEWHERE YOU DEFINE CONSTANT ONLY ONCE
let userDefaults = UserDefaults.standard
If you are using or any other method i dont know but if you get proper indexpath then this code will work
menuView.didSelectItemAtIndexHandler = {(indexPath: Int) -> Void in
var placesdeger: String = ""
userDefaults.removeObject(forKey: "indexPath")
if indexPath == 0 {
let userDefaults = UserDefaults.standard
UserDefaults.standard.setValue(indexPath, forKey: "indexPath")
placesdeger = "Yatak Odası"
}
if indexPath == 1 {
UserDefaults.standard.setValue(indexPath, forKey: "indexPath")
placesdeger = "Oturma Odası"
self.refreshData()
}
I've got an error on the let myValue line:
#IBAction func CAttamaran(_ sender: Any) {
// error happens here
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
UserDefaults.standard.set(myValue, forKey:"Shared room")
UserDefaults.standard.synchronize()
if let myOutput2: AnyObject = UserDefaults.standard.object(forKey: "Shared room") as AnyObject? {
// self.appDelegate.propertylabel = "\(myOutput2)" as NSString!
let secondViewController1 = self.storyboard!.instantiateViewController(withIdentifier: "propertyvc")
self.present(secondViewController1, animated: true, completion: nil)
print("property_id = \(myOutput2)")
}
}
This line
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
has three unwrappings in it, any one of which could be your problem
sender may not be castable as AnyObject
titleLabel might be nil especially if sender is an Objective-C object that doesn't have a titleLabel property.
titleLabel.text might be nil.
If you want to find out what the problem is, you need to do the unwrappings one at a time e.g.
guard let sender = sender as WhateverTypeYouThinkItShouldBe else { fatalError("sender is the wrong type") }
guard let titleLabel = sender.titleLabel else { fatalError("Title label is nil") }
if let text = titleLabel.text
{
// Do whatever you need to do
}
else
{
// There is no text in the label
}
The line of code you are pointing on, has a lot of issues:
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as NSString
First and first, force unwrapping like that is evil. It's the biggest source for possible crashes.
Why do you try to cast to AnyObject? That one doesn't have titleLabel property, so this wrong.
Why do you cast the text to NSString? Didn't notice any particular NSString API usage.
if let myOutput2: AnyObject = UserDefaults.standard.object(forKey: "Shared room") as AnyObject? {
You read again the same value from UserDefaults that you just saved.
If you rewrite your code in more Swift friendly way, it would be better:
Here is an example of your re-written function:
#IBAction func CAttamaran(_ sender: UIButton) {
guard let myValue = sender.titleLabel?.text else {
// Handle the edge case here
return
}
UserDefaults.standard.set(myValue, forKey:"Shared room")
UserDefaults.standard.synchronize()
guard let storyboard = self.storyboard else {
// Handle the edge case here
return
}
let secondViewController1 = storyboard.instantiateViewController(withIdentifier: "propertyvc")
self.present(secondViewController1, animated: true, completion: nil)
print("property_id = \(myValue)")
}
Any exclamation mark can cause that exception.
in your case
// problematic line is
let myValue:NSString = (sender as AnyObject).titleLabel!!.text! as
NSString
as we can see there is multiple chance of getting nil value
like
sender may not be castable as AnyObject
titleLabel might be nil
especially if sender is an Objective-C object that doesn't have a
titleLabel property.
titleLabel.text might be nil.
Solution for this is use guard statement like below
#IBAction func CAttamaran(_ sender: UIButton) {
guard let myValue = sender.titleLabel?.text else {
// Handle the edge case here
return
}
guard let storyboard = self.storyboard else {
// Handle the edge case here
return
}
// Rest of code
}
Hi, I am trying to save data in Userdefaults but whenever I run this page of the app I am getting Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOC) as shown in the image.
Prior to this, I was getting the same exact error on the line beneath the one that I am getting it on right now, but now it is coming on this current line as seen in the image. How can I fix this to save my data every time the app launches?
var highScore = 0
var bronzeStatus = 0
ViewDidLoad:
let defaults = UserDefaults.standard
highScore = defaults.value(forKey: "Add1High") as! NSInteger!
bronzeStatus = defaults.value(forKey: "bronzeMed") as! NSInteger!
Underneath:
if correctNumber > highScore {
highScore = correctNumber
let defaults = UserDefaults.standard
defaults.setValue(highScore, forKey: "Add1High")
defaults.synchronize()
}
if highScore >= 15 {
let defaults = UserDefaults.standard
let bronzeAlert = UIAlertController(title: "New Medal!", message: "You earned a bronze medal!", preferredStyle: UIAlertControllerStyle.alert)
bronzeAlert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(scoreAlert, animated: true, completion: nil)
bronzeStatus = 1
defaults.setValue(bronzeStatus, forKey: "bronzeMed")
defaults.synchronize()
}
Try using
highScore = defaults.integer(forKey: "Add1High")
it converts the returned value to an NSInteger. If the value is an NSNumber, the result of -integerValue will be returned. If the value is an NSString, it will be converted to NSInteger if possible. If the value is absent or can't be converted to an integer, 0 will be returned. So no need to convert on your part.
Also while setting use
defaults.set(highScore, forKey: "Add1High")
Swift 3.0 code
this code work parfect..
var highScore = 0
var bronzeStatus = 0
ViewDidLoad:
//set integer type data
UserDefaults.standard.set(20, forKey: "Add1High")
UserDefaults.standard.set(30, forKey: "bronzeMed")
//Get integer type data
let defaults = UserDefaults.standard
highScore = defaults.integer(forKey: "Add1High")
bronzeStatus = defaults.integer(forKey: "bronzeMed")
print("\(highScore)\(bronzeStatus)") //2030
use this to check your userdefaults value exits or not
let defaults = UserDefaults.standard
if defaults.value(forKey: "Add1High") != nil{
highScore = defaults.value(forKey: "Add1High") as! NSInteger!
}
if defaults.value(forKey: "bronzeStatus") != nil{
bronzeStatus = defaults.value(forKey: "bronzeStatus") as! NSInteger!
}
Because you have condition
if correctNumber > highScore{}
so this part may execute or maybe not ,so you have to check for that
I'm aware that this has been discussed in many other ways, but I have tried using previous answers and have remained stuck on this problem for over a week.
I am trying to gather questions from one plist file (with unique question IDs) and then saving that question ID with the user's answer into another plist.
I am having no issues loading the questions, but the following code doesn't seem to update the AnsweredQs plist file.
It prints "true" which suggests the write worked, but when I return to xcode there is no change to the plist.
func documentFilePath(fileName: String) -> String {
let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
let fileURL = documentsURL.appendingPathComponent(fileName)
return fileURL.path
}
func saveQuestion(ans: String) {
let path = documentFilePath(fileName: "AnsweredQs.plist")
let data = NSMutableDictionary()
data.setValue(QuestionView.questionID, forKey: "id")
data.setValue(ans, forKey: "answer")
data.setValue(Date(), forKey: "date")
data.setValue(loadQuestionData(id: QuestionView.questionID).expiry, forKey: "expiry")
data.setValue(loadQuestionData(id: QuestionView.questionID).maxAge, forKey: "max-age")
let didWriteSucess = data.write(toFile: path, atomically: false)
print(didWriteSucess)
print(data)
}
In addition to this, I am wanting to set up a check when the question is randomly generated so that only unanswered questions are selected for the user to answer. I've started work on this (although this code is not complete) and it doesn't seem to ever find anything in the AnsweredQs plist, suggesting further that the save isn't actually working.
func checkQuestion(id: Any) -> Bool {
var trueOrFalse = Bool()
if QuestionView.questionNumber >= 1 {
var questionDict: NSDictionary = [:]
if let url = Bundle.main.url(forResource:"AnsweredQs", withExtension: ".plist"),
let dict = NSDictionary(contentsOf: url) as? [String:Any] {
print(dict)
questionDict = dict as NSDictionary
let allValues = [questionDict]
if allValues.count != 0 {
if allValues.description == id as! String{
trueOrFalse = true
print("true\(allValues)")
} else {
trueOrFalse = false
print("false\(allValues)")
}
}
}
} else {
trueOrFalse = false
print("didn't check")
}
return(trueOrFalse)
It's my first app and I'm trying to figure it all out myself, but I've gotten myself so stuck that all I was left with was to ask here.
Thanks in advance x
I am trying to save inputs from various text fields using NSUserDefaults:
#IBAction func continue2save(sender: AnyObject) {
let stringy1: NSString = mcweightTF.text!
let stringy2: NSString = mcnumTF.text!
NSUserDefaults.standardUserDefaults().setObject(stringy1, forKey: "savemcw")
NSUserDefaults.standardUserDefaults().setObject(stringy2, forKey: "savemcn")
NSUserDefaults.standardUserDefaults().synchronize()
}
#IBAction func calculate(sender: AnyObject) {
let load1: AnyObject? = NSUserDefaults.standardUserDefaults().objectForKey("savemcw")
calcLabel.text = String(load1)
}
However "load1"'s value is always nil. I have attempted almost every configuration of implementing short-term storage through NSUserDefaults, however the value stored is always nil.
Try this,
#IBAction func continue2save(sender: AnyObject) {
let stringy1 = mcweightTF.text!
let stringy2 = mcnumTF.text!
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("stringy1", forKey: "savemcw")
defaults.setObject("stringy2", forKey: "savemcn")
}
#IBAction func calculate(sender: AnyObject) {
let defaults = NSUserDefaults.standardUserDefaults()
let stringy1 = defaults.stringForKey("savemcw")
// Optional Chaining for stringy1
if let stringy = stringy1 {
calcLabel.text = stringy
}
}