1

I am attempting to send an array of data to a new view controller and I'm currently getting the error fatal error: unexpectedly found nil while unwrapping an Optional value

Im using the API data from www.thecocktaildb.com

Example: http://www.thecocktaildb.com/api/json/v1/1/search.php?s=margarita

Not sure what I'm doing wrong. Tried debugging and checking values before the segue in my search view controller and they're accurate.

Heres my code:

Main Storyboard

enter image description here

SearchViewController

class SearchViewController: UIViewController, UISearchBarDelegate, UITableViewDataSource, UITableViewDelegate {

    @IBOutlet weak var TableView: UITableView!
    @IBOutlet weak var SearchBar: UISearchBar!

    var valueToPass:Drinks!
    var isSearching : Bool = false

    class Drinks {
        var idDrink: Int = 0
        var strDrink: String = ""
        var strCategory: String = ""

        var strAlcoholic: String = ""
        var strGlass: String = ""
        var strInstructions: String = ""

        var strDrinkThumb: String = ""

        var strIngredient1: String = ""
        var strIngredient2: String = ""
        var strIngredient3: String = ""
        var strIngredient4: String = ""
        var strIngredient5: String = ""
        var strIngredient6: String = ""
        var strIngredient7: String = ""
        var strIngredient8: String = ""
        var strIngredient9: String = ""
        var strIngredient10: String = ""
        var strIngredient11: String = ""
        var strIngredient12: String = ""
        var strIngredient13: String = ""
        var strIngredient14: String = ""
        var strIngredient15: String = ""

        var strMeasure1: String = ""
        var strMeasure2: String = ""
        var strMeasure3: String = ""
        var strMeasure4: String = ""
        var strMeasure5: String = ""
        var strMeasure6: String = ""
        var strMeasure7: String = ""
        var strMeasure8: String = ""
        var strMeasure9: String = ""
        var strMeasure10: String = ""
        var strMeasure11: String = ""
        var strMeasure12: String = ""
        var strMeasure13: String = ""
        var strMeasure14: String = ""
        var strMeasure15: String = ""
    }
    var TableData:Array< Drinks > = Array < Drinks >()

    override func viewDidLoad() {
        super.viewDidLoad()

        for subView in self.SearchBar.subviews
        {
            for subsubView in subView.subviews
            {
                if let textField = subsubView as? UITextField
                {
                    textField.attributedPlaceholder  = NSAttributedString(string: NSLocalizedString("Search", comment: ""))

                }
            }
        }

        self.SearchBar.delegate = self
        self.TableView.delegate = self
        self.TableView.dataSource = self
    }

    func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {

        if self.SearchBar.text!.isEmpty {

            self.isSearching = false

        }else{

            self.isSearching = true

            let userSearchInput = self.SearchBar.text!.lowercaseString
            let newString = userSearchInput.stringByReplacingOccurrencesOfString(" ", withString: "%20", options: NSStringCompareOptions.LiteralSearch, range: nil)

            let postEndpoint: String = "http://www.thecocktaildb.com/api/json/v1/1/search.php?s=" + newString

            guard let url = NSURL(string: postEndpoint) else {
                print("Error: cannot create URL")
                return
            }

            let urlRequest = NSURLRequest(URL: url)
            let config = NSURLSessionConfiguration.defaultSessionConfiguration()
            let session = NSURLSession(configuration: config)

            let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data, response, error) in
                guard let responseData = data else {
                    print("Error: did not receive data")
                    return
                }
                guard error == nil else {
                    print("error calling GET on www.thecocktaildb.com")
                    print(error)
                    return
                }

                let post: NSDictionary
                do {
                    post = try NSJSONSerialization.JSONObjectWithData(responseData,
                        options: []) as! NSDictionary
                } catch  {
                    print("error trying to convert data to JSON")
                    return
                }

                var count = 1
                if let drinks = post["drinks"] as? [NSDictionary] {
                    self.TableData.removeAll()
                    for drink in drinks {
                        let adrink = Drinks()
                        if let strDrink = drink["strDrink"] as? String {
                            print(String(count) + ". " + strDrink)
                            adrink.strDrink = strDrink
                            count++
                        }
                        if let strCategory = drink["strCategory"] as? String {
                            print("    Category: " + strCategory)
                            adrink.strCategory = strCategory
                        }
                        if let strDrinkThumb = drink["strDrinkThumb"] as? String {
                            print("    Thumbnail Image: " + strDrinkThumb)
                            adrink.strDrinkThumb = strDrinkThumb
                        }
                        self.TableData.append(adrink)
                        self.TableView.reloadData()
                    }
                }
            })
            task.resume()

        }
    }

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

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) 
        //title = TableData[indexPath.row].strDrink

        cell.textLabel?.text = TableData[indexPath.row].strDrink;

        let imageString = TableData[indexPath.row].strDrinkThumb

        if (imageString == ""){
            let noDrinkImage : UIImage = UIImage(named: "noimage.jpg")!
            cell.imageView!.image = noDrinkImage
        }else{
            let drinkImage =  UIImage(data: NSData(contentsOfURL: NSURL(string:TableData[indexPath.row].strDrinkThumb)!)!)
            cell.imageView!.image = drinkImage
        }
        return cell
    }

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
        print(TableData[indexPath.row].strDrink)
        valueToPass = TableData[indexPath.row]
        //self.performSegueWithIdentifier("drinkSegue", sender: TableData[indexPath.row])
    }

    // hide kwyboard when search button clicked
    func searchBarSearchButtonClicked(searchBar: UISearchBar) {
        self.SearchBar.resignFirstResponder()
    }

    // hide keyboard when cancel button clicked
    func searchBarCancelButtonClicked(searchBar: UISearchBar) {
        self.SearchBar.text = ""
        self.SearchBar.resignFirstResponder()
    }

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

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

        if (segue.identifier == "DrinkSegue") {
            // initialize new view controller and cast it as your view controller
            let drinkViewController = segue.destinationViewController as! DrinkViewController
            // your new view controller should have property that will store passed value
            drinkViewController.passedValue = valueToPass
        }
    }
}

DrinkViewController.swift

class DrinkViewController: UIViewController {

    @IBOutlet weak var DrinkNameLabel: UILabel!
    var passedValue : SearchViewController.Drinks!

    override func viewDidLoad() {
        super.viewDidLoad()
        DrinkNameLabel.text = passedValue!.strDrink
        // Do any additional setup after loading the view, typically from a nib.
    }

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

}

1 Answer 1

1

Do it like this instead

In your didSelectRowAtIndexPath pass the array

self.performSegueWithIdentifier("drinkSegue", sender: TableData[indexPath.row])

Here you need to pass the array to your DrinkViewController

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
     if (segue.identifier == "DrinkSegue") {
            // initialize new view controller and cast it as your view controller
            let drinkViewController = segue.destinationViewController as! DrinkViewController
            // your new view controller should have property that will store passed value
            drinkViewController.passedValue = valueToPass
            // declare myArray in your drinkViewController and then assign it here
            // now your array that you passed will be available through myArray
            drinkViewController.myArray = sender
        }
    }

Update
After I got your project I noticed that the issue you had was that you did drag a segue from the tableView to the drinksController directly - what happened is that didSelectRowAtIndexPath will not be called and your sender will always be nil drinkViewController.myArray = sender as! Drinks.

I changed that by dragging the segue from the viewController to the drinksController instead.

Sign up to request clarification or add additional context in comments.

6 Comments

Ok, I edited my code to drinkViewController.passedValue = sender and got the error: Cannot assign value of type 'AnyObject?' to type 'SearchViewController.Drinks!'
Try to add this drinkViewController.myArray = sender as! [the array type in DrinkViewController]
I added the line drinkViewController.passedValue = sender as! Drinks and received the error: Could not cast value of type 'UITableViewCell' to 'CocktailApp.SearchViewController.Drinks'. @Rashwan L
I put a breakpoint on the above line of code I added and the variable valueToPass (which should be the drink I click) is nil
@Zach here you go dropbox.com/s/xafu3g3u1ujxczv/CocktailApp-master.zip?dl=0 I´ll update the post with the errors that you had.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.