instantiateViewControllerWithIdentifier is crashing (found nil) - swift

When I try to instantiateViewControllerWithIdentifier on an iPhone it is crashing the app, although this works fine on the ios simulator. The code I have used is:
let questionsGameVC: QuestionsGame = self.storyboard?.instantiateViewControllerWithIdentifier("Questions") as! QuestionsGame
The error it is saying is
fatal error: unexpectedly found nil while unwrapping an Optional value
Can someone add anything to what is going wrong?

There are lots of places this could go wrong. I would put a breakpoint at that line and look at the state of the system, but debugging swift is still not in a great state. One alternative is to break apart that line of code and test all the pieces. Something like this:
if let storyboard = self.storyboard {
if let viewController = storyboard.instantiateViewControllerWithIdentifier("Questions") {
if let questionGame = viewController as? QuestionGame {
println("Success!")
} else {
println("Question game is the wrong type in the storyboard")
}
} else {
println("There is no view controller with the identifier Questions")
}
} else {
println("The storyboard is nil")
}
Whatever gets printed at the end should give you a better idea of where the problem is. Most often I have seen misspelled identifiers or situations where the class of the view controller in the storyboard has not been changed from UIViewController to the custom type.

Related

What is the purpose of a line of code that says: '_ = viewController.view'?

What is the purpose of the following line of code? It doesn't seem to do anything. If it were in Playground or in a print statement it would at least show something.
_ = masterVC.view
It's in Apple's sample code at Sharing CloudKit Data with Other iCloud Users.
Here's the complete significant code that contains that line of code:
if let masterVC = masterNC?.viewControllers.first as? ZoneViewController {
      _ = masterVC.view
      start ? masterVC.spinner.startAnimating() : masterVC.spinner.stopAnimating()
}
Let's see the doc of view from UIViewController:
If you access this property when its value is nil, the view controller automatically calls the loadView() method and returns the resulting view.
Seeing the project code: masterVC.spinner, is a lazy var that in viewDidLoad() will be added as a subview of its UITableView.
Why do this then?
Because when you do:
let someVC = SomeViewController()
Its IBOutlet and its view hasn't been loaded yet. You can reproduce it with getting having a IBOutlet on that VC, or some subview that will be added to the view in viewDidLoad(). Try to access the property, it will be nil for the IBOutlet (and usually crash with if it's declared forced unwrapped), and subview.superview will be nil for the other one.
That's also why when you use UIStoryboardSegue, in prepare(for:sender:), when you do:
if let nextVC = segue.destination as? SomeViewController() {
nextVC.labelIBOulet.text = "someTextToPass" //It will crash for the same reason
}
There, it's rather common to pass the model (here a simple String) instead of setting the value of the UILabel.
I personally prefer calling loadViewIfNeeded() instead, in my opinion it's less strange that invoking the view and ignore the returned value.

Typecasting for ViewControllers in swift

I’m quite new to iOS development and swift.
I’m seeing something like this quite often
let vc = window?.rootViewController as! MainViewController
Would someone mind explaining type casting and what the purpose of this is to me? I’ve been trying to find other questions or just a basic explanation of the purpose - but I’m not having much luck.
Thanks
The code you posted is a crash in the making. The as construct casts a variable of a general type to a more specific type. If it can fail, you need to add a ? (as?) or an exclamation point (as!) The form with an exclamation point is known as a "force cast". It tries to cast the object to a different type, and will crash if the cast fails. Thus it is a bad idea and should be avoided.
Better to use code like this:
func someFunc() {
guard let vc = window?.rootViewController as? MainViewController else {
return
}
//Code that depends on vc being cast to type MainViewController
}
(Read up on the guard statement in the Apple Swift iBook for more information.)
Even this code is better than what you posted:
func someFunc() {
guard let vc = window?.rootViewController as? MainViewController else {
fatalError("Unable to cast rootViewController to type MainViewController")
}
//Code that depends on vc being cast to type MainViewController
}
The second version will still crash, but will display a very clear message in the console when it does crash, so you can tell what went wrong.

How to call function from viewcontroller in appdelegate? (Swift)

So basically I am trying to run the function Refresh (Located in ViewController.swift) in AppDelegate.swift. After searching for the better part of 5 hours I can't figure out why this isnt working.
here is my function:
func Refresh(){
if(Count==0 && QuickActionChecker==true){
Previous.text=String(TipArray[CountCurrent])
Current.text=String(TipArray[CountCurrent])
Deliveries.text="\(Runs)"
}
if(Count>0 && QuickActionChecker==true){
Previous.text=String(TipArray[CountCurrent-1])
Current.text=String(Total)
Deliveries.text="\(Runs)"
}
}
In my Appdelegate.swift I have initialized the ViewController by setting it to Main:
let Main = ViewController()
and here is where I'm attempting to run the function from (force touch quick action from the homescreen):
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
if shortcutItem.type == "com.Sicarix.Ticket.Add5"{
if(CountCurrent==0){
TipArray.append(5)
Count=Count+1
CountCurrent=CountCurrent+2
Total=TipArray.reduce(0) {$0+$1}
Runs=Runs+1
QuickActionChecker=true
Main.Refresh()
}
else{
TipArray.append(5)
Count=Count+1
CountCurrent=CountCurrent+1
Total=TipArray.reduce(0) {$0+$1}
Runs=Runs+1
QuickActionChecker=true
Main.Refresh()
}
}
}
however, when I try to use the shortcut it opens the app to a white screen and stays there. Console is spitting out:
fatal error: unexpectedly found nil while unwrapping an Optional value
the code runs fine when I remove the Main.Refresh(), it just doesn't update the labels in my app, hence the need for the function.
Please help, I'm so ready to move on past this bug....
also please bear in mind that I haven't even been coding in swift for a week yet, so please break down what was wrong as best you can. TIA
Change your viewController object
let nextVC = storyboard?.instantiateViewController(withIdentifier:"ViewController") as! ViewController
The problem is that you are instantiating a new ViewController using ViewController() and that ViewController isn't added to your view controller hierarchy.
You need to use your Storyboard to instantiate the ViewController by using let mainVC = UIStoryboard(name: "Main", bundle: "nil").instantiateViewController(withIdentifier:"ViewController") as! ViewController and make sure your identifier is set up in Storyboard.
If you are not using Storyboard, another solution is to store the text you want to display on the UI in data source variables and only update those variables in 'AppDelegate', then load the content of those variables onto your labels in 'viewDidLoad' or 'viewWillAppear'.

AppDelegate, DidFinishLaunchingWithOptions, ImplicitlyUnwrappedOptional What does it mean?

The app build successfully with no error in debugging area, but immediately stops and bring me to this;
I'm not sure what to make of the error. I can only guess its in the AppDelegate.swift, somewhere along DidFinishLanchingWithOptions.
Does anyone know how to solve this error?
Sorry, not smart enough to figure out this probably trivial error to you guys
UPDATE:
I've tried the suggestion by user mstysf below, but the same problem occur? Am i doing it wrong or missing something?
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
if let navigationController = self.window?.rootViewController as? UINavigationController {
let challengesController = navigationController.topViewController as ChallengesViewController
unarchiveDataSource()
if let dataSource = challengeDataSource {
challengesController.challengeDataSource = dataSource
} else {
loadDefaultChallenges()
challengesController.challengeDataSource = challengeDataSource
}
}
return true
}
Does anyone know what is wrong? Thanks again, any help is appreciated.
It means the navigationController is nil. You should handle with it like this:
if let navigationController = self.window?.rootViewController as? UINavigationController {
// do your work here
}
It builds successfully because compiler trusts you navigationController won't be nil and does not check it. That's the point of implicitly unwrapped optional type. In the runtime if it is nil then your app crashes. That's the danger of implicitly unwrapped optionals.

Swift Optional Binding with a negative result

How can I do an optional binding in Swift and check for a negative result? Say for example I have an optional view controller that I'd like to lazy load. When it's time to use it, I'd like to check if it's nil, and initialize it if it hasn't been done yet.
I can do something like this:
if let vc = viewController? {
// do something with it
} else {
// initialize it
// do something with it
}
But this is kludgey and inefficient, and requires me to put the "do something with it" code in there twice or bury it in a closure. The obvious way to improve on this from objC experience would be something like this:
if !(let vc = viewController?) {
// initialize it
}
if let vc = viewController? {
// do something with it
}
But this nets you a "Pattern variable binding cannot appear in an expression" error, which is telling me not to put the binding inside the parenthesis and try to evaluate it as an expression, which of course is exactly what I'm trying to do...
Or another way to write that out that actually works is:
if let vc = viewController? {
} else {
// initialize it
}
if let vc = viewController? {
// do something with it
}
But this is... silly... for lack of a better word. There must be a better way!
How can I do an optional binding and check for a negative result as the default? Surely this is a common use case?
you can implicitly cast Optional to boolean value
if !viewController {
viewController = // something
}
let vc = viewController! // you know it must be non-nil
vc.doSomething()
Update: In Xcode6-beta5, Optional no longer conform to LogicValue/BooleanType, you have to check it with nil using == or !=
if viewController == nil {
viewController = // something
}
Would this work for you?
if viewController == nil {
// initialize it
}
// do something with it
One way might be to create a defer statement that handles the actions. We can ensure those actions occur after the creation of our object by checking for nil. If we run into nil, instantiate the object and return. Before returning our deference will occur within the scope of some function.
func recreateAndUse() {
defer {
viewController?.view.addSubview(UIView())
viewController!.beginAppearanceTransition(true, animated: true)
}
guard viewController != nil else {
viewController = UIViewController()
return
}
}