I have learned Swift and Xcode for about 3 weeks and I am doing an Todo App just for practice. But when I was trying to add new item to my todo list the error occurred saying that "Unexpectedly found nil while unwrapping an Optional value".
I have the main screen which is created by Main.storyboard and an add button on the main screen. When click that button it goes to a new ViewController which I created via the Eureka framework, user can enter some information like title, description, category in that form and pass them back to the main screen. I used the delegate protocol for passing data, when I click the Save Item button the app crashed and it said that
"Fatal error: Unexpectedly found nil while unwrapping an Optional value".
The code is below:
import Foundation
import Eureka
protocol CanReceive {
func dataReceived(data: ToDo)
}
class AddItemViewController : FormViewController {
var delegate : CanReceive?
var todoItem : ToDo?
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "MMM d yyyy, h:mm a"
return formatter
}()
override func viewDidLoad() {
super.viewDidLoad()
form +++ Section()
<<< TextRow(){
$0.title = "Title"
$0.placeholder = "Enter text here"
$0.onChange { [unowned self] row in
self.todoItem?.title = row.value
}
}
<<< TextRow(){
$0.title = "Description"
$0.placeholder = "Give some description"
$0.onChange { [unowned self] row in
self.todoItem?.description = row.value
}
}
<<< AlertRow<String>() {
$0.title = "Category"
$0.selectorTitle = "Select the category"
$0.options = ["Personal ", "Home ", "Work ", "Play ", "Health ♀️" , "Other"]
$0.onChange { [unowned self] row in
self.todoItem?.category = row.value
}
}
+++ Section(){ section in
section.header = {
var header = HeaderFooterView<UIView>(.callback({
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
button.backgroundColor = .darkGray
button.setTitle("Save Item", for: .normal)
button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)
return button
}))
header.height = { 50 }
return header
}()
}
}
@objc func buttonAction(sender: UIButton!) {
delegate?.dataReceived(data: todoItem!)
self.dismiss(animated: true, completion: nil)
print("Button tapped")
}
}
The Todo type is defined as:
struct ToDo {
var title: String?
var description: String?
var category : String?
}
And here is the ViewController that controls the main.storyboard :
import UIKit
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, CanReceive {
var todoList = [ToDo]()
let cellId = "CellId"
let test = ["Row1", "Row2", "Row3"]
let imageName = ["anchor", "arrow-down", "aperture"]
@IBOutlet weak var ImageTop: UIImageView!
@IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
self.tableView.register(UINib(nibName: "CustomTableViewCell", bundle: nil), forCellReuseIdentifier: cellId)
configureTableView()
ImageTop.image = UIImage(named: "todo-background")
}
//data source method
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return todoList.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CustomTableViewCell
cell.categoryLabel?.text = todoList[indexPath.row].category
cell.descriptionLabel.text = todoList[indexPath.row].description
var imageCategory = ""
switch todoList[indexPath.row].category {
case "Personal ":
imageCategory = "Personal"
case "Home ":
imageCategory = "Home"
case "Work ":
imageCategory = "Work"
case "Play ":
imageCategory = "Play"
case "Health ♀️":
imageCategory = "Health"
case "Other":
imageCategory = "Other"
default:
break
}
cell.imageCategory?.image = UIImage(named: imageCategory)
return cell
}
func configureTableView() {
self.tableView.rowHeight = 80
self.tableView.separatorStyle = .none
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
print("Did select row at index \(indexPath.row)")
print("Row height is: \(tableView.rowHeight)")
}
@IBAction func addButtonPressed(_ sender: RoundButton) {
let addItemViewController = AddItemViewController()
addItemViewController.delegate = self
self.present(addItemViewController, animated:true, completion:nil)
}
//Delegate
func dataReceived(data: ToDo) {
todoList.append(data)
return
}
}
Thank you for your time and sorry if my question is so silly.
The error occurs because in
AddItemViewControllerthe propertytodoItemis declared but not initialized.I recommend to use temporary variables for the three properties and create a
ToDoinstance when the button is pressed