I am trying to implement a "always logged in" function in to my app. The problem is that if I restart my app, it crashes. This is what I did:
set Userdefault:
@objc func loginButtonTapped(_ sender: Any) {
let email = self.emailTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
let password = self.passwordTextField.text!.trimmingCharacters(in: .whitespacesAndNewlines)
// start button animation
loginButton.startAnimation()
let qualityOfServiceClass = DispatchQoS.QoSClass.background
let backgorundQueue = DispatchQueue.global(qos: qualityOfServiceClass)
backgorundQueue.async {
// check if account details correct
Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
if error != nil {
DispatchQueue.main.async {
// error -> stop animation
self.loginButton.stopAnimation(animationStyle: .shake, revertAfterDelay: 0) {
self.errorLabel.text = error!.localizedDescription
self.errorLabel.alpha = 1
}
}
}else {
// correct acount details -> login
DispatchQueue.main.async {
UserDefaults.standard.set(true, forKey: "isLoggedIn")
UserDefaults.standard.synchronize()
// transition to home ViewController
self.transitionToHome()
}
}
}
}
}
checking UserDefault:
class MainNavigationControllerViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
if isLoggedIn() {
let homeController = MainViewController()
viewControllers = [homeController]
}
}
fileprivate func isLoggedIn() -> Bool {
return UserDefaults.standard.bool(forKey: "isLoggedIn")
}
}
The user logs in via Firebase and all the data is stored in Cloud Firestore.
Error
cell.customWishlistTapCallback = {
let heroID = "wishlistImageIDX\(indexPath)"
cell.theView.heroID = heroID
let addButtonHeroID = "addWishButtonID"
self.addButton.heroID = addButtonHeroID
// track selected index
self.currentWishListIDX = indexPath.item
let vc = self.storyboard?.instantiateViewController(withIdentifier: "WishlistVC") as? WishlistViewController
vc?.wishList = self.dataSourceArray[self.currentWishListIDX]
// pass drop down options
vc?.theDropDownOptions = self.dropDownButton.dropView.dropDownOptions
vc?.theDropDownImageOptions = self.dropDownButton.dropView.dropDownListImages
// pass current wishlist index
vc?.currentWishListIDX = self.currentWishListIDX
// pass the data array
vc?.dataSourceArray = self.dataSourceArray
// set Hero ID for transition
vc?.wishlistImage.heroID = heroID
vc?.addWishButton.heroID = addButtonHeroID
// allow MainVC to recieve updated datasource array
vc?.dismissWishDelegate = self
vc?.theTableView.tableView.reloadData()
self.present(vc!, animated: true, completion: nil)
}
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
at line:
let vc = self.storyboard?.instantiateViewController(withIdentifier: "WishlistVC") as! WishlistViewController
I guess it is not as easy as I thought. Does anyone know why the app crashes and how I can solve this? :)
You are creating your MainViewController
instance using a simple initialiser (MainViewController()
) rather than instantiating it from the storyboard. As a result, any @IBOutlet
properties will be nil
since it is the the storyboard process that allocates those object instances and assigns them to the properties.
You need to add an identifier to your main view controller scene (if it doesn't already have one) and use that to instantiate the view controller instance. E.g. assuming the scene identifier is "MainScene" you would have something like:
override func viewDidLoad() {
super.viewDidLoad()
if isLoggedIn() {
let homeController = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("MainScene")
viewControllers = [homeController]
}
}
The crash in your updated question indicates that either the scene with the identifier WishlistVC
doesn't have its class set to WishlistViewController
or it isn't found so the forced downcast crashes.