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

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.

Related

save/load TextField after it has been edited

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.

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")

How to populate table rows, using a [String] array sent from iPhone by Watch Connectivity?

Basically, it's passing the array fine. It's just when trying to use the enumerated array as the tablerows, it says nil found.
PHONE
import UIKit
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
#IBOutlet weak var sendButton: UIButton!
var watchSession: WCSession?
var arrayCustom = ["thing1", "thing2"]
override func viewDidLoad() {
super.viewDidLoad()
if(WCSession.isSupported()) {
watchSession = WCSession.defaultSession()
watchSession?.delegate = self
watchSession?.activateSession()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func sendArray(sender: AnyObject) {
sendToWatch()
}
private func sendToWatch() {
do {
let applicationDict = ["Array1": arrayCustom]
try WCSession.defaultSession().updateApplicationContext(applicationDict)
}
catch {
print(error)
}
}
}
WATCHKIT
private func loadThCust() {
if (WCSession.isSupported()) {
watchSession = WCSession.defaultSession()
watchSession.delegate = self;
watchSession.activateSession()]
}
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
dispatch_async(dispatch_get_main_queue()) { () -> Void in
if let retrievedArray1 = applicationContext["Array1"] as? [String] {
self.custArray = retrievedArray1
print(self.custArray)
}
for (index, thName) in self.custArray.enumerate() {
let row2 = self.choiceTable.rowControllerAtIndex(index) as! ChoiceTableRowController
row2.choiceLabel.setText(thName)
}
}
}
My problem is I'm getting this console output + error whenever I try and load the TableView:
["thing1", "thing2"]
2016-02-24 03:21:25.912 WristaRoo WatchKit Extension[9401:243561] Error - attempt to ask for row 0. Valid range is 0..0
fatal error: unexpectedly found nil while unwrapping an Optional value
Does anyone have any idea why the unwrapped value is still nil? I was hoping that once I get it set and can see that there are values in the [String] array, it could enumerate it, but it seems like they are just invisible.
You started the question with the statement, "Basically, It's passing the array fine" so it's not a Watch Connectivity issue.
You simply didn't specify the number of rows for choiceTable before iterating through your array.
choiceTable.setNumberOfRows(custArray.count, withRowType: "ChoiceTableRowController")
Determining the problem from the console output:
Error - attempt to ask for row 0. Valid range is 0..0
rowControllerAtIndex: returns (an Optional which is) nil when its index is out of bounds.
The row controller object, or nil if there are no row controllers yet or index is out of bounds.
You tried to access a row which didn't exist, leading to a bounds warning.
fatal error: unexpectedly found nil while unwrapping an Optional value
row2.choiceLabel.setText(thName)
row2 is nil.
You should be able to easily track down bugs like this in the debugger. If you examined row2 and saw that it was nil, you'd realize that the problem wasn't with the array itself, but that the table has no rows.

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

Swift: unexpectedly found nil while unwrapping an Optional value (lldb)

On this line: let task = self.session.venues.get(self.foursquareId!) {
import UIKit
import QuadratTouch
class VenueDetailsViewController: UIViewController {
#IBOutlet var venueLabelName: UILabel!
var foursquareId:String?
var session: Session!
override func viewDidLoad() {
super.viewDidLoad()
let task = self.session.venues.get(self.foursquareId!) {
(result) -> Void in
if result.response != nil {
if let venue = result.response!["venue"] as? JSONParameters {
if let venueName = venue["name"] as? String {
self.venueLabelName.text = venueName
}
}
} else {
// Show error.
}
}
task.start()
}
}
It throws me a
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)
Either session is nil, or foursquareId is nil. Since session is implicitly unwrapped, it will throw an error on that line as well as if foursquaerId is nil. You'll need to make sure these values are set before you attempt to access them like this.