Swift : fatal error: unexpectedly found nil and EXC_BAD_INSTRUCTION - ios

I m new to Xcode and i m building a project with a login button. After clicking the login with details in the text field, it will redirect to the second view which is the scroll view controller. However, i got two parts of "Error"
Normally, it work with a normal view controller(login and move to the second view). I have just recreate a view controller to a scroll view controller and it did not work.
By the way, I got a build success but i just got error when i try to "login"
Can anyone explain why i got the thread error and the fatal error?
How can i resolve the problem?
login view Controller
#IBAction func LoginBtn(sender: AnyObject) {
LoginIn()
}
func LoginIn(){
let user = PFUser()
user.username = usernameTF.text!
user.password = passwordTF.text!
PFUser.logInWithUsernameInBackground(usernameTF.text!,
password:passwordTF.text! , block: { (User: PFUser?, Error
:NSError? ) -> Void in
if Error == nil{
dispatch_async(dispatch_get_main_queue()){
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let MainVC : UIViewController = Storyboard.instantiateViewControllerWithIdentifier("scrollV") as UIViewController
self.presentViewController(MainVC, animated: true, completion: nil)
}
}
else{
NSLog("Wrong!!!!")
}
})
}
Scroll View Controller
import UIKit
class ScrollViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
override func viewDidLoad() {
super.viewDidLoad()
let V1 : ScrollViewController = ScrollViewController(nibName: "ScrollViewController", bundle: nil)
self.addChildViewController(V1)
self.scrollView!.addSubview(V1.view!)
V1.didMoveToParentViewController(self)
}

You are doing some forced unwrapping, which is not recommended (in general forced stuff in Swift is not recommended), and it will crash your application if the variable being unwrapped is nil. That's why forced unwrap is recommended only when you're 100% sure that you have a valid value in your optional.
In your case, it seems that V1.view! causes the crash, and this might be caused by the fact that V1 didn't successfully loaded from the nib.
Swift has made it very easy to work with nullable values. Just don't force unwrap and use optional binding instead.

#Cristik is correct. Instead of force unwrapping the variable, you should check to see if it is instantiated using the "if let" statement:
if let scrollView = self.scrollView {
if let subView = V1.view {
scrollView.addSubview(subView)
}
}

Related

Swift: Change ViewController after Authentication from RESTful API

I have a rails api set up and one test user. I can login/logout view the api.
I have a tab app set up, and I have a LoginViewController set up to authenticate into that. My view controller (basically) looks like the below code. This doesnt work. However, the second block of code does work
I am guessing I am calling something wrong.. The first block executes and then crashes with a *** Assertion failure in -[UIKeyboardTaskQueuewaitUntilAllTasksAreFinished].. I've been spinning my wheels for hours.. tried looking into segues.. etc. Anything would help!!!
//DOES NOT WORK //
class LoginViewController: UIViewController {
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
#IBAction func Login(sender: AnyObject) {
func switchToTabs() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
}
let authCall = PostData()
var authToken = ""
authCall.email = email.text!
authCall.password = password.text!
authCall.forData { jsonString in
authToken = jsonString
if(authToken != "") {
switchToTabs()
}
}
}
// SAME CLASS THAT DOES WORK//
class LoginViewController: UIViewController {
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
#IBAction func Login(sender: AnyObject) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let tabBarController = storyboard.instantiateViewControllerWithIdentifier("TabBarController") as! UITabBarController
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.window?.rootViewController = tabBarController
}
My question is, why can I seemingly navigate to a new view when I don't use my api, but when I do receive my auth token.. I can't log in.
Please excuse the ugly code.. this is my first day with swift
Everytime you every do anything that changed something about the UI inside of a closure or anything that could even possibly be off the main queue, enclose it in this statement.
dispatch_async(dispatch_get_main_queue()) {
//the code that handles UI
}
This includes all code that segues inside a closure like the one you have
authCall.forData { jsonString in
authToken = jsonString
if(authToken != "") {
switchToTabs()
}
}
If you write it like so it would work
authCall.forData {
jsonString in
authToken = jsonString
if(authToken != "") {
dispatch_async(dispatch_get_main_queue()) {
switchToTabs()
}
}
}
then call your segue in switchToTabs()
If you arn't sure what queue you are on, it won't hurt to do it. As you become more familiar with scenarios that take you off the main queue, you will realize when to use it. Anytime you are in a closure and dealing with UI its a safe bet that you could leave. Obviously anything asynchronous would cause it as well. Just place your performSegueWithIdentifier inside of it and it should work.
If you make a segue on the storyboard from the view controller attatched to LoginViewController, then you click the segue you can give it an identifier. Then you just call performSegueWithIdentifier("identifierName", sender: nil) and none of the other code in switchToTabs() is necessary.
If you want to continue down the route you are taking to summon the View Controller, then I think what you are missing is these lines.
Use this below your class outside of it.
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func tabBarController() -> UITabBarController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("TabBarController") as? UITabBarController
}
}
Then change your switchToTabs() function out for this
func switchToTabs() {
let tabBarController = UIStoryboard.tabBarController()
view.addSubview(tabBarController!.view)
//line below can be altered for different frames
//tabBarController!.frame = self.frame
addChildViewController(tabBarController!)
tabBarController!.didMoveToParentViewController(self)
//line below is another thing worth looking into but doesn't really correlate with this
//self.navigationController!.pushViewController(tabBarController!, animated: true)
}
I added in a couple comments for you to look into if you like. I believe this will do the job, but keep in mind that the direction you are headed doesn't remove the previous screen from memory. It is technically still sitting behind your new screen. I wouldn't ever recommend this for what you are trying to do, but it might not hurt if you are building something simplistic.

opening ViewController raise unexpectedly found nil while unwrapping an Optional value

I'm trying to open a new ViewController from my code by using
let registrationView = NewOrdoViewController()
self.presentViewController(registrationView, animated: true, completion: nil)
my NewOrdoViewController is containing a ScrollView and while opening the new View, my app crash at the following line:
override func viewDidLoad() {
super.viewDidLoad()
scrollView.contentSize.height = 2000 <-- it crash here
......
with the following error:
unexpectedly found nil while unwrapping an Optional value
is there any specific command to instantiate a new ViewController containing a ScrollView ?
You're opening a generic NewOrdoViewController, not the instance that exists in your storyboard. Give your NewOrdoViewController a storyboard ID in the interface builder and replace
let registrationView = NewOrdoViewController()
with
let registrationView = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("YourStoryboardID") as! NewOrdoViewController

Passing data between controllers - unexpectedly found nil while unwrapping an Optional

I am attempting a very simple task of passing data between two view controllers. I have control-dragged the label into the proper class and allowed the segues to populate with the correct data. Upon clicking the row, the prepareForSegue action is as:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "individualChat" {
if let indexPath = tableView.indexPathForSelectedRow {
let friend: Friend
friend = mappedFriends[indexPath.row]
let controller = segue.destinationViewController as! IndividualChatController
controller.friendChat = friend
}
}
}
and into the residing view controller:
import UIKit
class IndividualChatController: UIViewController {
#IBOutlet weak var anotherTestLabel: UILabel!
var friendChat: Friend! {
didSet {
configureView()
}
}
func configureView() {
if let friendChat = friendChat {
anotherTestLabel.text = friendChat.name as? String
}
}
}
Yet, upon clicking the row, the error appears:
fatal error: unexpectedly found nil while unwrapping an Optional value
on line:
anotherTestLabel.text = friendChat.name as? String
How can this be fixed?
Additional Screen Capture:
When you execute some code in prepareForSegue the next view is not loaded yet so all your outlets are still set to nil; because of this when you try to implicitly unwrap the content of anotherTestLabel at
anotherTestLabel.text = friendChat.name as? String
you get that error.
You can check if your label has been loaded and only if that's the case set its text using
anotherTestLabel?.text = friendChat.name as? String
and then manually calling configureView() in viewDidLoad() of IndividualChatController to proper load the label.
The added ? will check if your label is nil, if so nothing will be done, else it will proceed unwrapping the label and setting the text.

Swift: unexpectedly found nil while unwrapping an Optional value While updating a Label

I have a UILabel inside a modelviewcontroller.
What i want to do is change its text on a certain point of my code. The problem is it works in the first point and stops working on the second one, throwing the following error:
unexpectedly found nil while unwrapping an Optional value
I found other questions on the same error but those solutions are not working for me. Maybe I am doing something wrong with the optionals.
Here is the code of my model view controller:
import UIKit
class CheckInViewController: UIViewController {
#IBOutlet weak var test: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
print("Appeared")
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("scanViewController") as! ScanViewController
test.text = "Here it works" // FIRST POINT WORKS AND CHANGES THE LABEL
if let qrCode = vc.qrCode{ // vc.qrCode IS AN OPTIONAL
qrRead(qrCode)
vc.qrCode = nil
}
}
func qrRead(qrCode: String) {
print(qrCode)
test.text = "Here it doesnt work" // HERE IT STOPS WORKING
}
You are setting the vc.qrCode to nil directly after passing it to the func qrRead(qrCode: String) function.
Remove this line: vc.qrCode = nil and check if the error persists.
If it does not, as I suspect, nil the var out elsewhere.
Remember using if let, you actually access the underlying instance, not a copy.
As such, when you set vc.qrCode to nil, you are ALSO setting qrCode to nil which causes issues when you try to print it in the qrRead function
Example:
class Person {
}
var a:Person? = Person()
if let some = a
{
print(unsafeAddressOf(some))
print(unsafeAddressOf(a!))
}
Prints:
0x0000000005011ed0
0x0000000005011ed0
Which shows they are the same instance in memory.

tableView.reloadData() causes crash in Swift

I read a question asked on Stackoverflow about making a view controller transparent so when it is shown the parent view controller can still be seen behind. I was able to do so:
However now when I click add subject the table view does not add the subject to my tableview like so (this is the view that appears behind the above view when the "+" button is tapped):
but if i exit to my main menu and come back to the table view it displays the list like so (which is what I want when I tap "Add Subject).
I think this is due to tableView.reloadData() being in my ViewWillAppear function.
With this in my mind i added tableViewClass.tableView.reloadData to the "Add Subject" button action in order to reload data as soon as the button is pressed however I get fatal error: found nil while unwrapping optional value and it highlights tableViewClass.tableView.reloadData
I know that the parent view controller (one with the table view) is not killed once the New Subject view controller appears in order to show it behind. This is why "ViewWillAppear" is never called.
Im still a little confused as to why its crashing though... here is my code for my button:
#IBAction func btnAddTask_Click(sender: UIButton){
subMngr.addSubjectMonA(txtSubject.text, time1: txtTime.text, time2: txtTime2.text, col: monAview.ColorValue)
self.view.endEditing(true)
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
var context: NSManagedObjectContext = appDel.managedObjectContext!
var newCell = NSEntityDescription.insertNewObjectForEntityForName("SubjectsEntity", inManagedObjectContext: context) as NSManagedObject
newCell.setValue(txtTime.text, forKey: "starttime")
newCell.setValue(txtSubject.text , forKey: "title")
newCell.setValue(txtTime2.text , forKey: "endtime")
newCell.setValue(monAview.ColorValue, forKey: "color")
context.save(nil)
txtSubject.text = ""
txtTime.text = ""
txtTime2.text = ""
MondayAClass.TableView.reloadData() // ERROR HERE
dismissViewControllerAnimated(true, completion: nil)
}
Any help would be appreciated, thanks :)
EDIT
Here is my code to present the view controller and make it transparent:
#IBAction func addInfo(sender: AnyObject) {
let story: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let monAadd = story.instantiateViewControllerWithIdentifier("MondayAadd") as MondayAadd
monAadd.view.backgroundColor = UIColor.clearColor()
monAadd.modalPresentationStyle = UIModalPresentationStyle.Custom // If this line is taken out everything works fine
self.presentViewController(monAadd, animated: true, completion: nil)
}
fatal error: found nil while unwrapping optional value
This is Swift's way of saying that you attempted to access a nil value. Ensure that you have an owning relationship between the table view and its view controller, and this shouldn't happen.

Resources