I'm getting this error and unable to resolve it...
I'm passing an object from UIViewController to another through a UISegmentedControl button changes. Below is the code I'm using to pass the object. It is working great and I'm able to print the object details in the console.
@IBAction func switchViewAction(_ sender: UISegmentedControl) {
let vc = DirectionsViewController()
if let unwrappedRecipe = self.details
{
vc.customInit(recipes: unwrappedRecipe)
} else
{
print("it has no value!")
}
self.viewContainer.bringSubviewToFront(views[sender.selectedSegmentIndex])
}
However, the issue is when I'm trying to set a value to a label, I get the below error:
Unexpectedly found nil while implicitly unwrapping an Optional value
Below is the code I'm using inside DirectionsViewController
@IBOutlet weak var lblDirections: UILabel!
var recipe: Recipe? = nil
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
func customInit(recipes: Recipe)
{
lblDirections.text = recipes.name
}
I have researched about optional & forced variables, also tried to safely unwrap variables but with no luck. Can someone help pls?
If you have an IBOutlet
that is load from storyboard or xib, it is nil
until viewDidLoad
is called. So when func customInit(recipes: Recipe)
is called before viewDidLoad
, the lblDirections
is nil
, and because it is force unwrapped it will crash. You can fix this in a two way:
Ugly but easy. The viewDidLoad
is called when you first get view
of a view controller, so you can add _ = vc.view
just before vc.customInit(recipes: unwrappedRecipe)
in func switchViewAction(_ sender: UISegmentedControl)
. I'm not recommending using it production code, but you can use to test if everything is working fine.
Because you are using xib, you can initialize a view controller and provide custom init (even name of your method indicates it: customInit
). To have a proper view controller init you need to use:
class DirectionsViewController: UIViewController {
let recipe: Recipe
@IBOutlet weak var lblDirections: UILabel!
init(recipe: Recipe) {
self.recipe = recipe
let classType = type(of: self)
let bundle = Bundle(for:classType)
super.init(nibName: "DirectionsViewController", bundle: bundle) //provide your nib filename
}
override func viewDidLoad(){
super.viewDidLoad()
//code from func customInit(recipes: Recipe)
lblDirections.text = recipe.name
}
@available(*, unavailable, message: "use init(recipe: Recipe)")
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
fatalError("init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) has not been implemented")
}
@available(*, unavailable, message: "don't use sotryboard! use nib and init(recipe: Recipe)")
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
You can't provide optional Recipe
or update it after creating, but it makes code less complicated. It also assumes that your xib file is called DirectionsViewController.xib
(if not you need to change it in line super.init(nibName: "DirectionsViewController", bundle: bundle)
).
You can also use my micro-library NibBased to have a little less code to write. When using the library your code should be:
import NibBased
class DirectionsViewController: NibBaseViewController {
let recipe: Recipe
@IBOutlet weak var lblDirections: UILabel!
init(recipe: Recipe) {
self.recipe = recipe
super.init()
}
override func viewDidLoad() {
super.viewDidLoad()
lblDirections.text = recipe.name
}
}