0

I'm working on an app Witch has a list of places (added by the user) and a map view with a button to add the users curent location. the app works to this point. When the user taps on a place in the tableView the app should go to the mapView and show the place on the map with a pin . but when I do that the app crashes and gives me the following error unless the user has renamed the place and if they have, it works perfectly :

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

note that the crash occurs in the mapView on the line that says:

   let latitude = NSString(string: places[activePlace]["lat"]!).doubleValue

and that the

print("activePlace current value is \(activePlace)")

returns the number of the line in the TableView

How could I solve this? thanks !

Here is the code in the TableView:

import UIKit

var places = [Dictionary<String,String>()]

var activePlace = -1

class TableViewController: UITableViewController {

    func companyNameUpdatedAlert(title: String, error: String, indexPath: Int) {

        let alert = UIAlertController(title: title, message: error, preferredStyle: UIAlertControllerStyle.Alert)

        alert.addTextFieldWithConfigurationHandler { (textField) -> Void in

            textField.placeholder = "Enter new text"

        }

        alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { (action) -> Void in

            let lat = places[indexPath]["lat"]!

            let lon = places[indexPath]["lon"]!

            places.removeAtIndex(indexPath)

            places.insert(["name" : alert.textFields![0].text!, "lat" : lat, "lon" : lon], atIndex: indexPath)

            self.tableView.reloadData()

            NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
            NSUserDefaults.standardUserDefaults().synchronize()


        }))

        self.presentViewController(alert, animated: true, completion: nil)


    }

    override func tableView(tableView: UITableView, editActionsForRowAtIndexPath indexPath: NSIndexPath) -> [UITableViewRowAction]? {

        let changeText = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Change text" , handler: { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in

            self.companyNameUpdatedAlert("Update text", error: "enter text below", indexPath: indexPath.row)

        })

        let deleteAction = UITableViewRowAction(style: UITableViewRowActionStyle.Default, title: "Delete" , handler: { (action:UITableViewRowAction, indexPath:NSIndexPath) -> Void in

            places.removeAtIndex(indexPath.row)

            tableView.reloadData()

            NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
            NSUserDefaults.standardUserDefaults().synchronize()

        })

        return [changeText, deleteAction]


    }

    override func viewDidLoad() {

        //save start

        if NSUserDefaults.standardUserDefaults().objectForKey("places") != nil {

            places = NSUserDefaults.standardUserDefaults().objectForKey("places") as! [Dictionary]

            //test


            NSUserDefaults.standardUserDefaults().setObject(places, forKey: "places")
            NSUserDefaults.standardUserDefaults().synchronize()



            //save stop

        super.viewDidLoad()



        if places.count == 1 {

            places.removeAtIndex(0)

            places.append(["name":"go to map to add location","lat":"90","lon":"90"])



        }
        if NSUserDefaults.standardUserDefaults().objectForKey("places") != nil {

            places = NSUserDefaults.standardUserDefaults().objectForKey("places") as! [Dictionary]


        }

                }
    }

    override func didReceiveMemoryWarning() {

        super.didReceiveMemoryWarning()

    }

    override func numberOfSectionsInTableView(tableView: UITableView) -> Int {

        return 1

    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

        return places.count


    }

    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {

        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as UITableViewCell

        cell.textLabel?.text = places[indexPath.row]["name"]

        return cell



    }

    override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {

        activePlace = indexPath.row

        return indexPath

    }

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "newPlace" {

            activePlace = -1


        }

    }

    override func viewWillAppear(animated: Bool) {

        tableView.reloadData()


    }

}

and here is the code in the viewController ( mapView)

   if activePlace == -1 {


            manager.requestWhenInUseAuthorization()
            manager.startUpdatingLocation()

                } else {
       print("activePlace current value is \(activePlace)")


        let latitude = NSString(string: places[activePlace]["lat"]!).doubleValue

        let longitude = NSString(string: places[activePlace]["lon"]!).doubleValue

        let coordinate = CLLocationCoordinate2DMake(latitude, longitude)

        let latDelta:CLLocationDegrees = 0.01

        let lonDelta:CLLocationDegrees = 0.01

        let span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)

        let region:MKCoordinateRegion = MKCoordinateRegionMake(coordinate, span)

        self.Map.setRegion(region, animated: true)

        let annotation = MKPointAnnotation()

        annotation.coordinate = coordinate

        annotation.title = places[activePlace]["name"]

        self.Map.addAnnotation(annotation)

        print("activePlaces current value is (activePlaces)")
stephen D
  • 255
  • 1
  • 5
  • 16
  • 1
    You really like the force unwrap operator, don't you? – Alexander Jun 20 '16 at 15:59
  • It used to work when I did it without he button (long press) – stephen D Jun 20 '16 at 16:08
  • 1
    If you want to stop your program from crashing, then you should avoid using the crash operator `!` to deal with optionals. Instead you should learn how to safely unwrap with them, as detailed in the above Q&A I linked to. – Hamish Jun 20 '16 at 16:11
  • I do get this error , however , I do have an object, as it works once the place is renamed. @originaluser2 – stephen D Jun 20 '16 at 16:12
  • 1
    Please read the comprehensive community answer in the linked page. There's everything you need to know about this situation. – Eric Aya Jun 20 '16 at 16:23

2 Answers2

0

First of all, You can create custom class, container. Like UserData with required information like description and location. Second, You have, for free from framework, a container for location CLLocationCoordinate2D, use it. If You will have just array of simple objects, it will be easier to read, find bugs and maintain application.

Your error simply says that element of array that You are trying to get is nil. Remember that in Swift Arrays, Dictionaries and other containers are passed via value, not via reference, because they are STRUCTS!

And Your problem is in prepareForSegue, why You are not passing selected item here to destinationViewController? You should do that.

P.S. Do not use NSUserDefaults.standardUserDefaults() to store user data. You can use CoreData instead, it is pretty simple to use. You need just a little extra code in AppDelegate and some changes in TableView.

Prettygeek
  • 2,461
  • 3
  • 22
  • 44
0

The exclamation mark means: Dear compiler, I have an optional value. I am absolutely sure that it isn't nil. Should I be wrong, and should that optional value be nil when you unwrap it even though I was sure it isn't, then please crash and give me a fatal error "unexpectedly found nil while unwrapping optional value".

That's what you tell the compiler, and that's what you got. Where Objective-C lets you get away with things and maybe create strange behaviour, Swift doesn't.

Learn about "if let", guard statements, and in general read a good book about Swift.

gnasher729
  • 51,477
  • 5
  • 75
  • 98