save/load TextField after it has been edited - iphone

I have this app with a TextField and a Save Button that I want to save when I close after it has been edited and load when I open but I cannot figure it out.
I have written something but when I try to start there comes always the
error: "Fatal 1: Fatal error: Unexpectedly found nil while unwrapping
an Optional value"
This is the code (that is in the my ViewController) but I don't know if its the best way to do it:
#IBOutlet weak var numText: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
if UserDefaults.standard.object(forKey: "num") == nil {
numText.text = "15"
}
else {
let number = UserDefaults.standard.object(forKey: "num") as! String
numText.text = number
}
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func saveButtonPressed(_ sender: UIButton) {
if numText.text == nil {
UserDefaults.standard.set("15", forKey: "num")
}
else {
UserDefaults.standard.set(numText.text, forKey: "num")
}
}

Add the text as a computed Property in the ViewController:
var SAVEDTEXTKEY = "num"
var savedText: String? {
set{
guard let value = newValue else {
UserDefaults.standard.removeObject(forKey: SAVEDTEXTKEY)
UserDefaults.standard.synchronize()
return
}
UserDefaults.standard.set(value, forKey: SAVEDTEXTKEY)
UserDefaults.standard.synchronize()
}
get{
return UserDefaults.standard.string(forKey: SAVEDTEXTKEY)
}
}
After that, when you want to save just do :
self.savedText = self.numText?.text
And when you want to retrieve it just do:
self.numText.text = self.savedText
The problem I think is that you're not calling
UserDefaults.standard.synchronize()
after setting the value in user defaults.

Related

Found nil while unwrapping optional when accessing database using SQLite.Swift [duplicate]

This question already has answers here:
What does "Fatal error: Unexpectedly found nil while unwrapping an Optional value" mean?
(16 answers)
Closed 2 years ago.
I've got a SQL database using SQLite.Swift which I've got working with the functions I need in my main view controller. I've now tried to move the database, along with the functions, over to another class as I'll need to access it from another view controller (a total of two need access.
The database file and table are created fine but as soon as I go to view or modify the database I get:
// Unexpectedly found nil while implicitly unwrapping an Optional value
I've added the errors into the code where they occur. I'm new to Swift and am confused as everything works fine if the code is in the main view controller. Any help would be much appreciated!
My code for the class:
class testClass: UIViewController {
// db
var database: Connection!
//table
let starLinesTable = Table("starLinesTable")
// columns - "" = name
let id = Expression<Int>("id")
let date = Expression<String>("date")
let line = Expression<String>("line")
func createDBTable() {
// create db
do {
let documentDirectory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
let fileURL = documentDirectory.appendingPathComponent("starlineDB").appendingPathExtension("sqlite3")
let databse = try Connection(fileURL.path)
self.database = databse
} catch {
print("error creating documentDirectory")
}
// creates table ..................................................................................
let createTable = self.starLinesTable.create { (table) in
table.column(self.id, primaryKey: true)
table.column(self.date, unique: true)
table.column(self.line)
}
do {
try self.database.run(createTable)
print("created table")
} catch {
print("error creating table")
}
}
func functionTest() {
print("connection has been made")
}
func viewDate() {
do {
let viewToday = starLinesTable.filter(date == "today")
for i in try database.prepare(viewToday) { // Unexpectedly found nil while implicitly unwrapping an Optional value
print("id: \(i[id]). date: \(i[date]). line: \(i[line])")
}
} catch {
print(error)
print("error viewing today")
}
}
func viewAll() {
do {
let data = try self.database.prepare(self.starLinesTable) // Unexpectedly found nil while implicitly unwrapping an Optional value
for i in data {
print("id: \(i[self.id]). date: \(i[self.date]). line: \(i[self.line])")
}
} catch {
print(error)
print("couldn't view db")
}
}
func saveLine() {
let userDate = "dateInput.text"
let userLine = "this is a line"
let addLine = self.starLinesTable.insert(self.date <- userDate, self.line <- userLine)
do {
try self.database.run(addLine) // Unexpectedly found nil while implicitly unwrapping an Optional value
print("added new line")
} catch {
print(error)
print("didn't add new line")
}
}
}
My code for the view controller:
import UIKit
import SQLite
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
testClass().createDBTable()
}
#IBOutlet weak var dateInput: UITextField!
#IBOutlet weak var lineInput: UITextField!
#IBAction func saveButton(_ sender: Any) {
testClass().saveLine()}
#IBAction func viewAll(_ sender: Any) {
testClass().viewAll()}
#IBAction func viewTodayButton(_ sender: Any) {
testClass().viewDate()}
#IBAction func buttonFunctionTest(_ sender: Any) {
testClass().functionTest()}
}
This error is telling you that while you are expecting those fields to have values they actually don't. For example, in your try for the database in ViewDate, you are looking for viewToday but since that value is nil you app crashes. I would check to make sure your calls to the db are correct and if so make sure you have data there.
Also check the Apple documentation for unwrapping optionals so you don't get hard crashes but a simple error message.

Change app language programmatically WITHOUT restarting the app

When I change the language from my app it doesn't take effect until I close the app and restart it. I don't want to restart it. I tried also presenting rootViewController but doesn't work. Is there any solution?
You can change the language in every Single of ViewControllers inside ViewDidLoad() So when User Clicked on the purposed Language Save it with UserDefaults.standard.set("En", forKey: "languages") So when you've saved languages You can Get the Selected languages by UserDefaults.standard.String(forKey: "languages") Which will give you En String Value
override func viewDidLoad() {
super.viewDidLoad()
//Get Language Func
GetLanguage()
}
#IBAction func ChangeToEn(_ sender:Any) {
SaveSelectedLanguage(SomeString: "En")
}
func SaveSelectedLanguage(SomeString:String) {
//Save Selected Language
UserDefaults.standard.set("En", forKey: SomeString)
//GetResult By Get Language Func
GetLanguage()
}
func GetLanguage {
//initializing
Switch UserDefualts.standard.string(forkey:"languages") {
case "En":
lbl.text = "Hello World"
case "Tr":
lbl.text = "Merhaba Dunya"
case "Fa"
lbl.text = "سلام دنیا"
}
}
if you want to get the phone language once the app is lunch like in settings
class ViewController: UIViewController {
#IBOutlet weak var HeadTitle: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
//when You are trying to change the language
//Get Phone Language
GetPhoneLanguage()
//Get Language
GetLanguege()
}
#IBAction func SaveEn(_ sender: Any) {
SaveLanguage(Value: "en")
GetLanguege()
}
#IBAction func SaveTr(_ sender: Any) {
SaveLanguage(Value: "tr")
GetLanguege()
}
func SaveLanguage(Value:String){
UserDefaults.standard.set(Value, forKey: "AppleLanguages")
UserDefaults.standard.synchronize()
}
func GetPhoneLanguage(){
if UserDefaults.standard.string(forKey: "AppleLanguages") == nil {
let preferredLanguage = Locale.preferredLanguages[0] as String
print (preferredLanguage) //en-US
let arr = preferredLanguage.components(separatedBy: "-")
let deviceLanguage = arr.first
UserDefaults.standard.set(deviceLanguage!, forKey: "AppleLanguages")
}
}
func GetLanguege() {
switch UserDefaults.standard.string(forKey: "AppleLanguages") {
case "en":
HeadTitle.text = "Hello World"
case "tr":
HeadTitle.text = "Merhaba Dunya"
default:
print("No Languages")
}
}
}

How to ensure make sure I´m not accessing data until it´s loaded in?

I´m new at programming and I my code gets the error: fatal error: unexpectedly found nil while unwrapping an Optional value
2017-10-27 16:06:16.755817+0200 Inspireme1.0[836:85307] fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
New error:
fatal error: Index out of range
2017-10-27 19:08:05.488502+0200 Inspireme1.0[1262:771572] fatal error: Index out of range
(lldb)
I already looked it up here but I don´t know how to apply this in my case: How to ensure I'm not accessing outlets before they're loaded in
Here is my code:
var quotes: RandomItems! = RandomItems([
"Jonas",
"Mary",
"Michael",
"Jeff",
"Sarah",
])
#IBAction func PresentText(_ sender: Any) {
PresentingLabel.text = quotes.next() //<-- Error
}
struct RandomItems: Codable
{
var items : [String]
var seen = 0
init(items:[String], seen: Int)
{
self.items = items
self.seen = seen
}
init(_ items:[String])
{ self.init(items: items, seen: 0) }
mutating func next() -> String
{
let index = Int(arc4random_uniform(UInt32(items.count - seen)))
let item = items.remove(at:index) //<--Error
items.append(item)
seen = (seen + 1) % items.count
return item
}
func toPropertyList() -> [String: Any] {
return [
"items": items,
"seen": seen
]
}
}
var randomItems: RandomItems?
override func viewDidAppear(_ animated: Bool) {
// Code to load the struct again after the view appears.
let defaults = UserDefaults.standard
quotes = defaults.codable(RandomItems.self, forKey: "quotes")
}
override func viewWillDisappear(_ animated: Bool) {
// Code to save struct before the view disappears.
let defaults = UserDefaults.standard
if let quotes = quotes {
defaults.set(codable: quotes, forKey: "quotes")
}
}
}
Quotes should not be optional.
You need to handle the optional outcome in viewDidAppear rather than assuming quotes will always unwrap.
quotes = defaults.codable(RandomItems.self, forKey: "quotes") ??
RandomItems([])
In viewDidLoad:
defaults.set(codable: quotes, forKey: "quotes")

UserDefaults checking for nil value not working

Any idea why my if statement always gives else answer?
I noticed from the print that the object is stored as "optional()", maybe this optional state is different from nil?
class ViewController: UIViewController {
#IBOutlet weak var phoneLabel: UITextField!
#IBOutlet weak var phoneDisplayLabel: UILabel!
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set("\(phoneLabel.text!)", forKey: "Nb")
print(UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
let phoneNB = UserDefaults.standard.value(forKey: "Nb")
if let phoneNbDisplay = phoneNB as? String {
if UserDefaults.standard.object(forKey: "Nb") != nil {
phoneDisplayLabel.text = "Your NB is \(phoneNbDisplay)"
}
else {
phoneDisplayLabel.text = "enter a number first"
}
}
}
}
The recommended way (by Apple!) is to register key/value pairs in UserDefaults to provide – as the class name implies – default values. Those default values are considered until the value is changed the first time.
The Swift benefit is that there is always a (non-optional) value which can be unwrapped safely.
As soon as possible (e.g. applicationWillFinishLaunching) add these lines. They are supposed to be executed every time the application launches. The key Nb is registered with an empty string default value.
let defaultValues = ["Nb" : ""]
UserDefaults.standard.register(defaults: defaultValues)
In storeButton don't remove the key and don't use String Interpolation for a String but check if the text property is nil and save an empty string in this case. The print line is nonsensical because reading from UserDefaults right after writing will not get the changes and you know what's been written
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.set(phoneLabel.text ?? "", forKey: "Nb")
}
In retrieveButton get the string with the dedicated method string(forKey, unwrap it (due to registering the key there is always a value) and only check for empty string. And don't read the (same) value 3 times.
#IBAction func retrieveButton(_ sender: Any) {
let phoneNbDisplay = UserDefaults.standard.string(forKey: "Nb")!
if phoneNbDisplay.isEmpty {
phoneDisplayLabel.text = "enter a number first"
} else {
phoneDisplayLabel.text = "Your NB is \(phoneNbDisplay)"
}
}
As vadian commented, you should not and have no need to use KVC method value(forKey:) in this case.
Try this:
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set(phoneLabel.text ?? "", forKey: "Nb") //<-You have no need to use String Interpolation.
print (UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
if let phoneNB = UserDefaults.standard.string(forKey: "Nb") {
if !phoneNB.isEmpty {
phoneDisplayLabel.text = "Your NB is \(phoneNB)"
} else {
phoneDisplayLabel.text = "enter a number first"
}
} else {
//### What do you want to do if "Nb" is not set?
}
}
try this
if (UserDefaults.standard.value(forKey: "etc") != nil)
{
if (UserDefaults.standard.value(forKey: "etc") as! String).character.count == 0
{
//value is ""
}else{
//having some value
}
}
You might forget to call synchronize while storing value.You can try this.
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set("\(phoneLabel.text!)", forKey: "Nb")
UserDefaults.standard.synchronize()
print (UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
if let phoneNB = UserDefaults.standard.string(forKey: "Nb"){
phoneDisplayLabel.text = "Your NB is \(phoneNB)"
}else {
phoneDisplayLabel.text = "enter a number first"
}
}

Call a func from another class in swift

I would like to call a function which is coded on another class.
So far I have made a struct on the file structs.swift for my data:
struct defValues {
let defCityName: String
let loadImages: Bool
init(defCity: String, loadImgs: Bool){
self.defCityName = defCity
self.loadImages = loadImgs
}
}
I have made the file Defaults.swift containing:
import Foundation
class DefaultsSet {
let cityKey: String = "default_city"
let loadKey: String = "load_imgs"
func read() -> defValues {
let defaults = NSUserDefaults.standardUserDefaults()
if let name = defaults.stringForKey(cityKey){
print(name)
let valuesToReturn = defValues(defCity: name, loadImgs: true)
return valuesToReturn
}
else {
let valuesToReturn = defValues(defCity: "No default city set", loadImgs: true)
return valuesToReturn
}
}
func write(city: String, load: Bool){
let def = NSUserDefaults.standardUserDefaults()
def.setObject(city, forKey: cityKey)
def.setBool(load, forKey: loadKey)
}
}
in which I have the two functions read, write to read and write data with NSUsersDefault respectively.
On my main ViewController I can read data with:
let loadeddata: defValues = DefaultsSet().read()
if loadeddata.defCityName == "No default city set" {
defaultCity = "London"
}
else {
defaultCity = loadeddata.defCityName
defaultLoad = loadeddata.loadImages
}
But when I try to write data it gives me error. I use this code:
#IBOutlet var settingsTable: UITableView!
#IBOutlet var defaultCityName: UITextField!
#IBOutlet var loadImgs: UISwitch!
var switchState: Bool = true
#IBAction func switchChanged(sender: UISwitch) {
if sender.on{
switchState = true
print(switchState)
}else {
switchState = false
print(switchState)
}
}
#IBAction func saveSettings(sender: UIButton) {
DefaultsSet.write(defaultCityName.text, switchState)
}
You need an instance of the DefaultsSet class
In the view controller add this line on the class level
var setOfDefaults = DefaultsSet()
Then read
let loadeddata = setOfDefaults.read()
and write
setOfDefaults.write(defaultCityName.text, switchState)
The variable name setOfDefaults is on purpose to see the difference.
Or make the functions class functions and the variables static variables and call the functions on the class (without parentheses)
From the code you posted, it seems you either need to make the write method a class method (just prefix it with class) or you need to call it on an instance of DefaultsSet: DefaultsSet().write(defaultCityName.text, switchState).
Another issue I found is that you also need to unwrapp the value of the textField. Your write method takes as parameters a String and a Bool, but the value of defaultCityName.text is an optional, so String?. This results in a compiler error.
You can try something like this:
#IBAction func saveSettings(sender: UIButton) {
guard let text = defaultCityName.text else {
// the text is empty - nothing to save
return
}
DefaultsSet.write(text, switchState)
}
This code should now compile and let you call your method.
Let me know if it helped you solve the problem