enable a button if matches the last date stored +1 - swift

I am fairly new to Swift, but getting better.
I have managed to disable a button and store the date. And at this point I have reached the end of my knowledge, so I am hoping someone can help.
The button then needs to be enabled the next day by checking against the date stored, so the user can only use the function once per day.
code is as follows;
import Foundation
import UIKit
class tryForAFiver : UIViewController {
#IBOutlet weak var drinkImage: UIImageView!
#IBOutlet weak var redeemButton: UIButton!
override func viewDidLoad() {
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
#IBAction func redeemButton(_ sender: Any) {
let cocktailNumber = arc4random_uniform(32)
drinkImage.image = UIImage(named: "cocktailList\(cocktailNumber)")
let userDefaults = UserDefaults.standard
if var timeList = userDefaults.object(forKey: "timeList") as? [NSDate]
{
timeList.append(NSDate())
userDefaults.set(timeList, forKey: "timeList")
}
else
{
userDefaults.set([NSDate()], forKey: "timeList")
}
userDefaults.synchronize()
if let timeList = UserDefaults.standard.object(forKey: "timeList") as? [NSDate]
{
print(timeList)
}
self.redeemButton.isEnabled = false
}
}
thanks in advance for any help.

I made some changes to your code. Is it OK to use Date() instead of NSDate()? It's easier to work with in Swift.
Button action:
#IBAction func redeemButton(_ sender: Any) {
let userDefaults = UserDefaults.standard
if var timeList = userDefaults.object(forKey: "timeList") as? [Date]
{
timeList.append(Date())
userDefaults.set(timeList, forKey: "timeList")
}
else
{
userDefaults.set([Date()], forKey: "timeList")
}
userDefaults.synchronize()
if let timeList = UserDefaults.standard.object(forKey: "timeList") as? [Date]
{
print(timeList)
}
self.redeemButton.isEnabled = false
}
And on viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
if let timeList = UserDefaults.standard.object(forKey: "timeList") as? [Date], let lastDay = timeList.last
{
if Calendar.current.isDateInToday(lastDay) {
self.redeemButton.isEnabled = false
}
else {
self.redeemButton.isEnabled = true
}
}
}
This should get you on the right track. A word of warning: neither UserDefaults() nor Date() are safe for doing this kind of thing. Both are easily modified by the client. You should do a server check also if it's important.

Related

Swift change language in runtime without restarting the app

The code I wrote changes the language only if I restart the app, but I would like it to change language immediately.
It's possible to do it?
The language switch works fine, but only after I restart the app.
Maybe the problem is in the extension string?
Localizable.strings (it)
"label.language" = "Lingua";
Localizable.strings (en)
"label.language" = "Language";
struct ConstantFile {
static let labelLanguage = NSLocalizedString("label.language".localized, comment: "")
}
extension String {
var localized: String {
if let _ = UserDefaults.standard.string(forKey: "UserDefaultLanguage") {} else {
// setting value default
UserDefaults.standard.set(Language.italian.rawValue, forKey: "UserDefaultLanguage")
UserDefaults.standard.synchronize()
}
let lang = UserDefaults.standard.string(forKey: "UserDefaultLanguage")
let path = Bundle.main.path(forResource: lang, ofType: "lproj")
let bundle = Bundle(path: path!)
return NSLocalizedString(self, tableName: nil, bundle: bundle!, value: "", comment: "")
}
}
class ViewController: UIViewController {
#IBOutlet weak var labelTitleLanguage: UILabel!
let userDefaults = UserDefaults.standard
let LANGUAGE_KEY = "UserDefaultLanguage"
override func viewDidLoad() {
super.viewDidLoad()
labelTitleLanguage.text = ConstantFile.labelLanguage
}
#objc func tapBtnConfirmation(_ sender: UIButton) {
let list = list[self.pickerView.selectedRow(inComponent: 0)]
buttonPickerViewLanguage.setTitle(list, for: .normal)
if list == ConstantFile.labelLanguageItalian {
userDefaults.set(Language.italian.rawValue, forKey: LANGUAGE_KEY)
} else if list == ConstantFile.labelLanguageEnglish {
userDefaults.set(Language.english.rawValue, forKey: LANGUAGE_KEY)
}
}
}
You'll need to have a notification sent when changing language.
Then in your views, you observe this notification, and when it's raised, you update all translated texts in the view.
That's what I do

Displaying Username in Swift and FireBase

I'm trying to display the users username that stored in firebase. I'm not sure if Im saving the username correctly or if that's even the problem. I can't seem to access the data or display it at all. Any tips? I have a screenshot of the Cloud Firestore attached.
SignupViewController
import UIKit
import FirebaseAuth
import Firebase
import AVKit
import FirebaseFirestore
class SignUpViewController: UIViewController {
var videoPlayer:AVPlayer?
var videoPlayerLayer:AVPlayerLayer?
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var BackButton: UIButton!
#IBOutlet weak var UserNameTextField: UITextField!
#IBOutlet weak var signUpButton: UIButton!
#IBOutlet weak var errorLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: view, action: #selector(UIView.endEditing))
view.addGestureRecognizer(tap)
// Do any additional setup after loading the view.
setupElements()
}
func setupElements(){
//hides the error label
errorLabel.alpha = 0
//styles the text and buttons
Utilities.styleTextField(UserNameTextField)
Utilities.styleTextField(firstNameTextField)
Utilities.styleTextField(lastNameTextField)
Utilities.styleTextField(passwordTextField)
Utilities.styleTextField(emailTextField)
Utilities.styleFilledButton(signUpButton)
}
/*
// MARK: - Navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
// this function makes sure all data is good
func validateFields() -> String?
{
//make sure fields are filled
if firstNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" || lastNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" || emailTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == "" || UserNameTextField.text?.trimmingCharacters(in: .whitespacesAndNewlines) == ""
{
return "Please fill all fields"
}
let cleanPassword = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
if Utilities.isPasswordValid(cleanPassword) == false{
return "Needs 8 charaters, number, and special charater"
}
return nil
}
#IBAction func signUpTapped(_ sender: Any) {
//validate fields
let error = validateFields()
if error != nil{
showError(error!)
}
else{
let firstName = firstNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let lastName = lastNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let email = emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let username = UserNameTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
//create user
Auth.auth().createUser(withEmail: email, password: password) { result, err in
if err != nil{
self.showError("Error creating user")
}
else
{
//User is stored sucessfully, store the first and last name
let database = Firestore.firestore()
database.collection("UserInfo").addDocument(data: ["firstname": firstName,"email": email, "lastname": lastName, "username": username, "uid": result!.user.uid]) {(error) in
if error != nil
{
self.showError("First name and last name couldnt be saved.")
}
}
self.transitionToHome()
}
}
}
}
func showError(_ message:String){
errorLabel.text = message
errorLabel.alpha = 1
}
func transitionToHome(){
let homeViewController = storyboard?.instantiateViewController(identifier: Constants.StoryBoard.homeViewController) as?
HomeViewController
view.window?.rootViewController = homeViewController
view.window?.makeKeyAndVisible()
}
override func viewWillAppear(_ animated: Bool) {
//Set up video in background
setUpVideo()
}
func setUpVideo(){
//Get path to resource bundle
let bundlePath = Bundle.main.path(forResource: "IMG_7211 2", ofType: "mov")
guard bundlePath != nil else{
return
}
//create the url from it
let url = URL(fileURLWithPath: bundlePath!)
//Create The video Player item
let item = AVPlayerItem(url: url)
//create the player
videoPlayer = AVPlayer(playerItem: item)
//create the layer
videoPlayerLayer = AVPlayerLayer(player: videoPlayer!)
//adjust the size and frame
videoPlayerLayer?.frame = CGRect(x: -self.view.frame.size.width*1.5, y:0, width: self.view.frame.size.width*4, height: self.view.frame.size.height)
view.layer.insertSublayer(videoPlayerLayer!, at: 0)
//add and play
videoPlayer?.playImmediately(atRate: 0.8)
}
}
CurrentUser.swift
import Foundation
struct CurrentUser{
let uid: String
let name: String
let email: String
let profilepictureURL:String
init(uid: String, dictionary: [String: Any]){
self.uid = uid
self.name = dictionary["username"] as? String ?? ""
self.email = dictionary["email"] as? String ?? ""
self.profilepictureURL = dictionary["profilepictureURL"] as? String ?? ""
}
}
ProfileViewController
import UIKit
import Firebase
import FirebaseAuth
import FirebaseInstallations
import FirebaseFirestore
import FirebaseStorage
import FirebaseDatabase
class ProfileViewController: UIViewController {
#IBOutlet weak var SignOut: UIButton!
#IBOutlet weak var UserName: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
UserNameLabel()
// Do any additional setup after loading the view.
}
func UserNameLabel()
{
if Auth.auth().currentUser != nil
{
guard let uid = Auth.auth().currentUser?.uid else {return}
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value) { (snapshot) in
guard let dict = snapshot.value as? [String: Any] else {return}
let user = CurrentUser(uid: uid, dictionary: dict)
self.UserName.text = user.username
}
}
}
#IBAction func SignOut(_ sender: Any) {
if Auth.auth().currentUser != nil {
do {
try Auth.auth().signOut()
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "Vc")
vc.modalPresentationStyle = .fullScreen
present(vc, animated: true, completion: nil)
}
catch let error as NSError {
print(error.localizedDescription)
}
}
}
}
The problem is you are saving the data to Firebase Firestore, and then trying to access it from firebase real time database. Firebase Realtime Database and Firebase Firestore are 2 completely different backend servers and you are trying to use both at the same time. You need to pick only 1 and stick with it.
You are saving it to firebase firestore by using:
let database = Firestore.firestore()
database.collection("UserInfo").addDocument()
then after you save it to firestore you are trying to access it from real time database, not Firestore which is causing it to return nil:
Database.database().reference().child("users").child(uid).observeSingleEvent(of: .value) { (snapshot) in
So instead what you need to do is get it back from firestore by using this:
Firestore.firestore().collection("s").getDocuments { query, error in
for doc in query!.documents {
// Successfully retrieved data from firestore.
}
}
Note: You could also instead save it to the real time database instead of firestore, but it just depends which one will work better for your app. You need to pick either Firestore or the Database, you can't use both. Here is more info to help you decide which one is better. More Info

app reverts all input variables to defaults after some time

I wrote my fist app, very simple, that creates a user defined number of badges at random times during a user defined window of time. It works fine but after some time (not sure how long, 2-4 hours), all of the user input information reverts to the defaults of the program. The issue is it is supposed to run each day but it is annoying to have to set it each morning. I am not sure if this is a coding issue or if the app 'reboots' when it is not doing anything in the background. Note that this occurs on my iPhone 8 but not on the simulator (or I am not patient enough for it to occur on the simulator).
I have put several print and label to try to identify when it occurs; I am sure I am putting them in the correct places. I apologize for including so much code - I tried to weed some of the mistakes out but I do not know where the problem is.
import UserNotifications
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var EarlyTimePicker: UITextField!
#IBOutlet weak var LateTimePicker: UITextField!
#IBOutlet weak var NumQuestions: UITextField!
#IBOutlet weak var myLabel_Questions: UILabel!// Attached to the label box
#IBOutlet weak var myLabel_StartEndTime: UILabel!
#IBOutlet weak var myLabel_TestResetTime: UILabel!
#IBOutlet weak var myLabel_CurrentEarlyTime: UILabel!
private var earlyTimePicker: UIDatePicker?
private var lateTimePicker: UIDatePicker?
override func viewDidLoad() {
super.viewDidLoad()
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .badge, .sound]) { (granted, error) in //ask for permission in order to show messages on the lock screen
if granted {
print("Yay!")
} else {
print("D'oh")
}
}
earlyTimePicker = UIDatePicker()
earlyTimePicker?.datePickerMode = .time //change to .time
earlyTimePicker?.addTarget(self, action: #selector(ViewController.earlyTimeChanged(earlyTimePicker:)),for: .valueChanged)
lateTimePicker = UIDatePicker()
lateTimePicker?.datePickerMode = .time //change to .time
lateTimePicker?.addTarget(self, action: #selector(ViewController.lateTimeChanged(lateTimePicker:)),for: .valueChanged)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.viewTapped(gestureRecognizer:)))
view.addGestureRecognizer(tapGesture)
EarlyTimePicker.inputView = earlyTimePicker
LateTimePicker.inputView = lateTimePicker
}
#objc func viewTapped(gestureRecognizer: UITapGestureRecognizer){
view.endEditing(true)
}
var earlyTime=480
var earlyTimehour=0
var earlyTimeminute=0
#objc func earlyTimeChanged(earlyTimePicker: UIDatePicker){
let earlyTimeFormatter = DateFormatter()
earlyTimeFormatter.dateFormat = "h:mm a"
earlyTimeFormatter.amSymbol = "AM"
earlyTimeFormatter.pmSymbol = "PM"
EarlyTimePicker.text = earlyTimeFormatter.string(from: earlyTimePicker.date)
view.endEditing(true)
let earlyTimedate = earlyTimePicker.date
let earlyTimecomponents = Calendar.current.dateComponents([.hour, .minute], from: earlyTimedate)
earlyTimehour = earlyTimecomponents.hour!
earlyTimeminute = earlyTimecomponents.minute!
earlyTime = earlyTimecomponents.hour! * 60 + earlyTimecomponents.minute!
print("earlyTimehour: \(earlyTimecomponents.hour!)")
print("earlyTimeminute: \(earlyTimecomponents)")
print("earlyTime: \(earlyTime)")
print("Current Time: \(Date())")
}
var lateTime=1200
var lateTimehour=0
var lateTimeminute=0
#objc func lateTimeChanged(lateTimePicker: UIDatePicker){
let lateTimeFormatter = DateFormatter()
lateTimeFormatter.dateFormat = "h:mm a"
lateTimeFormatter.amSymbol = "AM"
lateTimeFormatter.pmSymbol = "PM"
LateTimePicker.text = lateTimeFormatter.string(from: lateTimePicker.date)
view.endEditing(true)
let lateTimedate = lateTimePicker.date
let lateTimecomponents = Calendar.current.dateComponents([.hour, .minute], from: lateTimedate)
lateTimehour = lateTimecomponents.hour!
lateTimeminute = lateTimecomponents.minute!
lateTime = lateTimecomponents.hour! * 60 + lateTimecomponents.minute!
let testMinute = lateTime % 60
let testHour = lateTime / 60
print("lateTimehour: \(lateTimecomponents.hour!)")
print("lateTimeminute: \(lateTimecomponents)")
print("lateTime: \(lateTime)")
print("testHour: \(testHour)")
print("testMinute: \(testMinute)")
myLabel_TestResetTime.text = "Time Set \(Date())"
myLabel_CurrentEarlyTime.text = "Current Early Time: \(earlyTime) / OnOff: \(OnOff)"
}
let PickedString = ["One","Two","Three","Four", "Five","Six","Seven","Eight"]
// #IBAction func TestCallFunction(_ sender: UIButton) {
// scheduleLocal()
// }
//NEED TO REPEAT THIS FUNCTION AT EARLY TIME - 10
//need to stop repeating with a cancel button (while bool true, do it, while false, stop. Default is false)
var RunDaily: Timer?
var OnOff = false
var QuestionNum = 1
#IBAction func Launch(_ sender: UIButton) {
OnOff = true
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
guard let QuestionNumA = Int(NumQuestions.text!) else { //This is how to get the UserInterface VALUE as a number
print("not a number!: \(String(describing: NumQuestions.text))")
return
}
print("Number of Questions: \(QuestionNumA)")
// var QuestionNum = 1
if QuestionNumA > 10 {QuestionNum=10} else {QuestionNum=QuestionNumA}
print("QuestionNumA:\(QuestionNumA) vs QuestionNum: \(QuestionNum)")
printStuff()
showMessage()
}
#IBAction func Stop(_ sender: UIButton) {
OnOff = false
printStuff()
}
func printStuff() {
if OnOff == true {
print("Bool is On : \(OnOff)")
RunDaily = Timer.scheduledTimer(timeInterval: 86400, target: self, selector: #selector(showMessage), userInfo: nil, repeats: true)//86400
}
if OnOff == false {
print("Bool is Off : \(OnOff)")
RunDaily?.invalidate()
let center = UNUserNotificationCenter.current()
center.removeAllPendingNotificationRequests()
}
}
func SaveDefaultData(){ // THis is the structure to SAVE input data for when the app relaunches (causes error when run.
let defaults = UserDefaults.standard
defaults.set("Date()", forKey:"key1")
//defaults.set(earlyTimePicker, forKey:"earlyTimePickerSet") cannot set earlyTimePicker. causes crash
// defaults.set(lateTimePicker, forKey:"lateTimePickerSet")
defaults.set(earlyTime, forKey:"earlyTimeSet")
defaults.set(lateTime, forKey:"lateTimeSet")
defaults.set(QuestionNum, forKey:"QuestionNumSet")
}
func SetDefaultData(){// THis is the structure to Set input for when the app relaunches
let defaults = UserDefaults.standard
if let savedValue = defaults.string(forKey: "key1"){
print("Here you will get saved value \(savedValue)")
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "key1")
}
if let earlyTimeValue = defaults.string(forKey: "earlyTimeSet"){
print("Here you will get saved value \(earlyTimeValue)")
earlyTime = UserDefaults.standard.value(forKey: "earlyTimeSet") as? Int ?? 485
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "earlyTimeSet")
earlyTime = 500
}
if let lateTimeValue = defaults.string(forKey: "lateTimeSet"){
print("Here you will get saved value \(lateTimeValue)")
lateTime = UserDefaults.standard.value(forKey: "lateTimeSet") as? Int ?? 1265
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "lateTimeSet")
lateTime = 1230
}
if let QuestionNumValue = defaults.string(forKey: "QuestionNumSet"){
print("Here you will get saved value \(QuestionNumValue)")
QuestionNum = UserDefaults.standard.value(forKey: "QuestionNumSet") as? Int ?? 4
} else {
print("No value in Userdefault,Either you can save value here or perform other operation")
defaults.set("Here you can save value", forKey: "QuestionNumSet")
QuestionNum = 2
}
}
#objc func showMessage() {
let center = UNUserNotificationCenter.current()
if lateTime <= earlyTime {
lateTime = earlyTime+1
if earlyTimehour <= 12 {
LateTimePicker.text = "\(earlyTimehour):\(earlyTimeminute) AM"
}
if earlyTimehour > 12 {
let EarlyTimeAfternoon = earlyTimehour - 12
LateTimePicker.text = "\(EarlyTimeAfternoon):\(earlyTimeminute) PM"
}
}
// center.removeAllPendingNotificationRequests()
// THIS IS WHERE ALL THE USER INPUT GETS INTO THE PROGRAM //
// guard let QuestionNumA = Int(NumQuestions.text!) else { //This is how to get the UserInterface VALUE as a number
// print("not a number!: \(String(describing: NumQuestions.text))")
// return
// }
//print("Number of Questions: \(QuestionNumA)")
// THIS IS WHERE ALL THE USER INPUT GETS INTO THE PROGRAM //
var RandHourArray:[Int] = [0]
var RandMinArray:[Int] = [0]
var RandQuestionArray:[Int] = [0]
var Counter = 1
// var QuestionNum = 1
//if QuestionNumA > 2 {QuestionNum=10} else {QuestionNum=QuestionNumA}
// print("QuestionNumA:\(QuestionNumA) vs QuestionNum: \(QuestionNum)")
for _ in 0 ... QuestionNum-1{
// Pick random times for badges
//let RandHour = Int.random(in: earlyTimehour ... lateTimehour)
let RandTimeMinFromMidnight = Int.random(in: self.earlyTime ... self.lateTime)
let ConvertRandTimeHours = RandTimeMinFromMidnight / 60
let ConvertRandTimeMinutes = RandTimeMinFromMidnight % 60
RandHourArray.append(ConvertRandTimeHours)
//let RandMin = Int.random(in: earlyTimeminute ... lateTimeminute)
RandMinArray.append(ConvertRandTimeMinutes)
let RandQuestion = Int.random(in: 0 ... self.PickedString.count-1)
RandQuestionArray.append(RandQuestion)
//print("RandTimeMinFromMidnight: \(RandTimeMinFromMidnight)")
// print("RandHourArray: \(RandHourArray)")
// print("ConvertRandTimeHours: \(ConvertRandTimeHours)")
// print("RandMinArray: \(RandMinArray)")
// print("ConvertRandTimeMinutes: \(ConvertRandTimeMinutes)")
}
myLabel_Questions.text = "# of questions: \(QuestionNum)"//\(QuestionNumA)"
myLabel_StartEndTime.text = "Start Time \(earlyTime) / End Time \(lateTime)"
let content_A = UNMutableNotificationContent()
content_A.title = "Prompt"
content_A.body = self.PickedString[RandQuestionArray[Counter]] //
content_A.categoryIdentifier = "alarm"
content_A.userInfo = ["customData": "fizzbuzz"]
content_A.sound = UNNotificationSound.default
var dateComponents_A = DateComponents()
dateComponents_A.hour = RandHourArray[Counter]
dateComponents_A.minute = RandMinArray[Counter]
let trigger_A = UNCalendarNotificationTrigger(dateMatching: dateComponents_A, repeats: false)
let request_A = UNNotificationRequest(identifier: UUID().uuidString, content: content_A, trigger: trigger_A)
center.add(request_A)
print("Request A time: \(RandHourArray[Counter]) : \(RandMinArray[Counter])")
print("Question String picked A: \(self.PickedString[RandQuestionArray[Counter]])")
Counter=2
if Counter<=QuestionNum {
let content_B = UNMutableNotificationContent()
content_B.title = "Prompt"
content_B.body = self.PickedString[RandQuestionArray[Counter]]
content_B.categoryIdentifier = "alarm"
content_B.userInfo = ["customData": "fizzbuzz"]
content_B.sound = UNNotificationSound.default
var dateComponents_B = DateComponents()
dateComponents_B.hour = RandHourArray[Counter]
dateComponents_B.minute = RandMinArray[Counter]
let trigger_B = UNCalendarNotificationTrigger(dateMatching: dateComponents_B, repeats: false)
let request_B = UNNotificationRequest(identifier: UUID().uuidString, content: content_B, trigger: trigger_B)
center.add(request_B)
print("Request B time: \(RandHourArray[Counter]) : \(RandMinArray[Counter])")
print("Question String picked B: \(self.PickedString[RandQuestionArray[Counter]])")
}
}
}
You should store your data inside UserDefaults, Keychain and Core Data or other stuff. if you dont store your data every time you close the application all the data will deallocate from the memory because they were stored in the heap.
Unsaved data:
let myLabel: UILabel = UILabel()
myLabel.text = "Some text"
Should save like:
UserDefaults.standard.setValue(myLabel.text, forKey: "it.is.custom")
And load like:
myLabel.text = UserDefaults.standard.value(forKey: "it.is.custom") as? String
refrence to study: https://fluffy.es/persist-data/

lost data in the coreData

My app will show teacher shedule, in the first controller teacher need to choose the right pair (first, second, third) and then write nessesary data (like what the subject name, room, number of group) now in my code if i write data in first pair and save, it will show to me, but if i write data in second pair and choose save button, the previous data in first pair is lost, so i have a problem with losing data, this is code for controller where i choose pair:
class ChoosePair: UIViewController {
enum choosePair: Int{
case FirstPair = 21
case SecondPair = 22
case ThirdPair = 23
case FourPair = 24
}
#IBAction func PairButtons(_ sender: UIButton){
guard let day = choosePair.init(rawValue: sender.tag) else { return }
switch day {
case .FirstPair:
let FirstPair = self
UserDefaults.standard.set(21, forKey: "pairId")
performSegue(withIdentifier: "SheduleTeacher", sender: self)
break
case .SecondPair:
let SecondPair = self
UserDefaults.standard.set(22, forKey: "pairId")
performSegue(withIdentifier: "SheduleTeacher", sender: SecondPair)
break
case .ThirdPair:
let ThirdPair = self
UserDefaults.standard.set(23, forKey: "pairId")
performSegue(withIdentifier: "SheduleTeacher", sender: ThirdPair)
break
case .FourPair:
let FourPair = self
UserDefaults.standard.set(24, forKey: "pairId")
performSegue(withIdentifier: "SheduleTeacher", sender: FourPair)
break
}
}
}
And the code for second controller(where i write data and save it in CoreData)
class SheduleTeacher: UIViewController {
var pairId = UserDefaults.standard.integer(forKey: "pairId")
#IBOutlet weak var subjectLabel: UILabel!
#IBOutlet weak var roomLabel: UILabel!
#IBOutlet weak var timeLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
#IBOutlet weak var groupLabel: UILabel!
#IBOutlet weak var captainLabel: UILabel!
#IBOutlet weak var subjectField: UITextField!
#IBOutlet weak var roomField: UITextField!
#IBOutlet weak var timeField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var groupField: UITextField!
#IBOutlet weak var captainField: UITextField!
let appDelegate = UIApplication.shared.delegate as! AppDelegate
override func viewDidLoad() {
super.viewDidLoad()
let context = appDelegate.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
do {
let result = try context.fetch(request)
for data in result as! [NSManagedObject] {
subjectField.text = data.value(forKey: "subject\(pairId)") as? String
roomField.text = data.value(forKey: "room\(pairId)") as? String
timeField.text = data.value(forKey: "time\(pairId)") as? String
emailField.text = data.value(forKey: "emailGroup\(pairId)") as? String
groupField.text = data.value(forKey: "group\(pairId)") as? String
captainField.text = data.value(forKey: "captain\(pairId)") as? String
}
} catch {
print("Failed")
}
subjectLabel.layer.borderWidth = 1.0
roomLabel.layer.borderWidth = 1.0
timeLabel.layer.borderWidth = 1.0
emailLabel.layer.borderWidth = 1.0
groupLabel.layer.borderWidth = 1.0
captainLabel.layer.borderWidth = 1.0
}
#IBAction func saveButton(_ sender: UIButton) {
let context = appDelegate.persistentContainer.viewContext
let entity = NSEntityDescription.entity(forEntityName: "Users", in: context)
let newUser = NSManagedObject(entity: entity!, insertInto: context)
newUser.setValue(self.subjectField!.text, forKey: "subject\(pairId)")
newUser.setValue(self.roomField!.text, forKey: "room\(pairId)")
newUser.setValue(self.timeField!.text, forKey: "time\(pairId)")
newUser.setValue(self.emailField!.text, forKey: "emailGroup\(pairId)")
newUser.setValue(self.groupField!.text, forKey: "group\(pairId)")
newUser.setValue(self.captainField!.text, forKey: "captain\(pairId)")
do {
try context.save()
} catch {
print("Failed saving")
}
}
}
And photo what I now have in CoreData
photo
Predicates are used to limit the fetch to only get certain entities.
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
Does not set a predicate so it always returns all the Users entities there are. The following has you iterate over every entity, that's why there is a for-loop.
for data in result as! [NSManagedObject] {
Here is an example how you can use a predicate to only get entities with entity.pairId == pairId.
override func viewDidLoad() {
super.viewDidLoad()
let context = AppDelegate.shared.persistentContainer.viewContext
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
request.returnsObjectsAsFaults = false
request.predicate = NSPredicate(format: "pairId = %#", argumentArray: [pairId])
do {
let results = try context.fetch(request)
guard results.count == 1 else {
fatalError("Bad count: \(results.count)")
}
guard let data = results.first! as? NSManagedObject else {
fatalError("No NSManagedObject")
}
context.performAndWait {
subjectField.text = data.value(forKey: "subject\(pairId)") as? String
roomField.text = data.value(forKey: "room\(pairId)") as? String
timeField.text = data.value(forKey: "time\(pairId)") as? String
emailField.text = data.value(forKey: "emailGroup\(pairId)") as? String
groupField.text = data.value(forKey: "group\(pairId)") as? String
captainField.text = data.value(forKey: "captain\(pairId)") as? String
}
} catch {
print("Failed")
}
// ...
}
The entity must of course have said attribute pairId for the predicate to work.
You would do something like this in the save() method too. There you could update the retrieved Users entity/create a new one if one does not exist.
One alternative is to delete the old one if one so exists and always create a new one on save.
The other alternative to "create a new one if one does not exist" is to pre-create for all four buttons and then fetch the entities on save and update them there. You would however have to check on startup too if they exist already else you would create on every startup four.
In your saveButton func you create a new Users instance every time which mean data from pair two will not be written together with data for pair one but as a separate record. You need to update the existing object, the one you fetched in viewDidLoad, instead. (I very much doubt any data is lost)
That being said I think you need to rethink your design, having duplicate (or more) fields in the same entity looks like an awkward design that will be hard to maintain. A better solution would be to only have one group attribute, one subject attribute and so on and complement it with an attribute that holds the pairId.
Also, why is the entity name Users, it seems to hold no kind of user id?

NSDictionary becomes NSCFString when I save to NSUserDefaults in Swift

NSDictionary becomes NSCFString when I save to NSUserDefaults:
class SecondViewController: UIViewController {
#IBOutlet var questionforsave: UITextField!
#IBOutlet var answerforsave: UITextField!
var answersSaved = [String:String]()
#IBAction func savePressed(sender: AnyObject) {
if questionforsave.text.isEmpty || answerforsave.text.isEmpty{
let alert = UIAlertView()
alert.title = "Empty fields"
alert.message = "Please Enter Text In Both Text Fields"
alert.addButtonWithTitle("Ok")
alert.show()
}else{
println("is ok")
var answerSave = answerforsave.text
var questionSave = questionforsave.text
answersSaved[questionSave] = answerSave
let alert = UIAlertView()
alert.title = "Success!"
alert.message = "It worked!"
alert.addButtonWithTitle("Awesome!")
alert.show()
NSUserDefaults.standardUserDefaults().setObject(answersSaved, forKey: "answersSaved")
println(answersSaved)
}
}
override func viewDidLoad() {
super.viewDidLoad()
println(answersSaved)
if NSUserDefaults.standardUserDefaults().objectForKey("answersSaved") != nil {
answersSaved = NSUserDefaults.standardUserDefaults().objectForKey("answersSaved") as! [String:String]
}else{
println("No Defaults")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
self.view.endEditing(true)
}
func textFieldShouldReturn(textField: UITextField!) -> Bool {
//do the stuff here
resignFirstResponder()
return true
}
}
Your issue is that you are setting a string and then trying to retrieve a dictionary, but I would recommend using if let to retrieve the dictionary and then optionally cast it to be [String: String]:
var answersSaved = [String: String]()
override func viewDidLoad() {
super.viewDidLoad()
if let answersFromDefaults = NSUserDefaults.standardUserDefaults().dictionaryForKey("answersSaved") as? [String: String] {
answersSaved = answersFromDefaults
} else {
// Failed to retrieve value from NSUserDefaults
}
answersSaved = ["test": "a"] // Set answersSaved somewhere in your code
}
#IBAction func saveButtonPressed(sender: UIBarButtonItem) {
NSUserDefaults.standardUserDefaults().setObject(answersSaved, forKey: "answersSaved")
}
NSUserDefaults.standardUserDefaults().setObject("answersSaved", forKey: "answersSaved")
You're writing the string "answersSaved" into NSUserDefaults. I assume you meant answersSaved.
You should cast it optionally as a [String:String] dictionary.
Try this:
if let answersFromDefaults = NSUserDefaults.standardUserDefaults().dictionaryForKey("answersSaved") as? [String: String] {
answersSaved = answersFromDefaults
} else {
// Failed to retrieve value from NSUserDefaults
}
When you load the dictionary into NSUserDefaults you should do it like this:
NSUserDefaults.standardUserDefaults().setObject(answersSaved, forKey: "answersSaved")
This saves it as a generic NSObject. When you want to read out the data though use the command:
answersSaved = NSUserDefaults.standardUserDefaults().dictionaryForKey("answersSaved")
This should specify that the object is a Dictionary