Đoàn Thanh An Đoàn Thanh An - 2 months ago 8
iOS Question

fatal error: unexpectedly found nil while unwrapping an Optional value when load the value from Firebasedatabase

I am pretty new with swift. I tried to finger out myself. I know this is the common question, but I hope I could get help. When I run the application. I got the "fatal error: unexpectedly found nil while unwrapping an Optional value".

import Foundation
import Firebase
import FirebaseDatabase
import FirebaseStorage
import FirebaseAuth

struct TodoItemDatabase {
var eventID: String!
var title: String!
var staff: String!
var location: String!
var starts: String!
var ends: String!
var rpeat: String!
var imageName: String!
var description: String!
var secondPhoto: String!
var ref: FIRDatabaseReference?
var key: String!
var isCompleted: Bool

init (eventID: String!, title: String,staff:String, location: String,starts: String, ends: String, rpeat: String, imageName: String, description: String, secondPhoto: String, key: String = "", isCompleted: Bool){
self.eventID = eventID
self.title = title
self.staff = staff
self.location = location
self.starts = starts
self.ends = ends
self.rpeat = rpeat
self.imageName = imageName
self.description = description
self.secondPhoto = secondPhoto
self.key = key
self.ref = FIRDatabase.database().reference()
self.isCompleted = isCompleted
}
init(snapshot: FIRDataSnapshot){

**//I get the error from here. However, I think the main reason in tableview below**

self.eventID = snapshot.value!["eventID"] as! String
self.title = snapshot.value!["title"] as! String
self.staff = snapshot.value!["staff"] as! String
self.location = snapshot.value!["location"] as! String
self.starts = snapshot.value!["starts"] as! String
self.ends = snapshot.value!["ends"] as! String
self.rpeat = snapshot.value!["rpeat"] as! String
self.imageName = snapshot.value!["imageName"] as! String
self.description = snapshot.value!["description"] as! String
self.secondPhoto = snapshot.value!["secondPhoto"] as! String
self.key = snapshot.key
self.ref = snapshot.ref
self.isCompleted = snapshot.value!["isCompleted"] as! Bool
}
func toAnyObject() -> [String: AnyObject] {
return ["eventid": eventID, "title": title, "staff": staff, "location": location, "starts": starts, "ends": ends, "rpeat": rpeat, "imageName": imageName, "description": description, "secondPhoto": secondPhoto, "isCompleted": isCompleted]
}


}

However, When I run the application, and load the tableview. It appears that error.

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
import FirebaseStorage

var toDoList:[TodoItemDatabase] = [TodoItemDatabase]()

class CurrentEventViewController: UIViewController, UITableViewDelegate {

var databaseRef: FIRDatabaseReference!{
return FIRDatabase.database().reference()
}

var storageRef: FIRStorageReference!

@IBOutlet var toDoListTable: UITableView!

override func viewDidLoad() {
super.viewDidLoad()



}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return toDoList.count

}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! myCell
let todoItem = toDoList[indexPath.row]
storageRef = FIRStorage.storage().referenceForURL(toDoList[indexPath.row].imageName)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) in

if error == nil {
dispatch_async(dispatch_get_main_queue(), {
if let data = data {
cell.myImageView.image = UIImage(data: data)
}

})
} else {
print(error!.localizedDescription)
}


}
cell.myLabel.text = todoItem.title!

return cell


}


func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) {

if editingStyle == UITableViewCellEditingStyle.Delete {

let ref = toDoList[indexPath.row]
ref.ref?.removeValue()
toDoList.removeAtIndex(indexPath.row)



toDoListTable.reloadData()
}

}


override func viewDidAppear(animated: Bool) {

toDoListTable.reloadData()

}
override func viewWillAppear(animated: Bool) {
let postRef = FIRDatabase.database().reference().child("posts").queryOrderedByChild("isCompleted").queryEqualToValue(false)
postRef.observeEventType(.Value, withBlock: { (snapshot) in
var newPosts = [TodoItemDatabase]()
for post in snapshot.children{
**// I think the reason is the line after.**
let post = TodoItemDatabase(snapshot: post as! FIRDataSnapshot)
newPosts.insert(post, atIndex: 0)
}
toDoList = newPosts
dispatch_async(dispatch_get_main_queue(), {
self.toDoListTable.reloadData()
})
}) { (error) in
print(error.localizedDescription)
}
}


func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {

storageRef = FIRStorage.storage().referenceForURL(toDoList[indexPath.row].imageName)
let storageRef1 = FIRStorage.storage().referenceForURL(toDoList[indexPath.row].secondPhoto)
let itemSelected = toDoList[indexPath.row]
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) in
if error == nil
{
dispatch_async(dispatch_get_main_queue(), {
if let data = data
{
storageRef1.dataWithMaxSize(1 * 1024 * 1024) { (data1, error) in

if error == nil
{
dispatch_async(dispatch_get_main_queue(), {
if let data1 = data1
{
let detailVC:DetailViewController = self.storyboard?.instantiateViewControllerWithIdentifier("DetailViewController") as! DetailViewController
detailVC.titleEvent = itemSelected.title
detailVC.staffEvent = itemSelected.staff
detailVC.locationEvent = itemSelected.location
detailVC.startEvent = itemSelected.starts
detailVC.endEvent = itemSelected.ends
detailVC.repeatEvent = itemSelected.rpeat
detailVC.imageDetail = UIImage(data: data)!
detailVC.descriptionDetail = itemSelected.description
detailVC.secondPhotoEvent = UIImage(data: data1)!
detailVC.key = itemSelected.key
self.presentViewController(detailVC, animated: true, completion: nil)
}
})
}
else
{
print(error!.localizedDescription)
}

}}
})
}
else
{
print(error!.localizedDescription)
}

}
}


}

Answer

You need to conditionally unwrap or nil coalesce these values. Force unwrapping the way you are is not safe.

self.eventID = snapshot.value!["eventID"] as! String

should probably be

eventID = snapshot.value?["eventID"] as? String ?? ""

Unfortunately, the compiler loves to suggest force-unwrapping when it encounters optional values. This is almost always a terrible suggestion. You need to get into the habit of handling Optionals gracefully when you encounter .None since Optionals are such an integral part of the Swift language.