AppDelegate, DidFinishLaunchingWithOptions, ImplicitlyUnwrappedOptional What does it mean? - swift

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.

Related

Receive SIGABRT error when trying to hijack root view controller

I am trying to have my application open a different view controller based upon whether an array is empty in the user's NSUserDefaults. Essentially, if the user has previously saved data in the app, the app will open up to where they can select the data. Otherwise, the app will open to a welcome screen.
However, when the array is empty, I see the background color that I set for the welcome screen, but not the text or button that I laid out in the storyboard. When the array is not empty and the data page should open, my app crashes with a SIGABRT error. I checked all of the outlets for the view controller in question and nothing seems to be disconnected. Additionally, when I comment out the code in the app delegate and set the data view controller as my initial starting view, the app runs fine.
The full error is "Thread 1: signal SIGABRT" and it is tagged in the class AppDelegate line.
The code I used in the App Delegate is below:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
var accounts = loadAccounts()!
if accounts.isEmpty {
let welcomeController = WelcomeViewController()
self.window!.rootViewController = welcomeController
} else {
let tableController = AccountTableViewController()
self.window!.rootViewController = tableController
}
return true
}
func loadAccounts() -> [Account]? {
return NSKeyedUnarchiver.unarchiveObject(withFile: Account.ArchiveURL.path) as? [Account]
}
Maybe the UIWindow is not set properly.
let bounds = UIScreen.main.bounds
self.window = UIWindow(frame: bounds)
self.window?.rootViewController = `your view controller`
self.window?.makeKeyAndVisible()
What else can go wrong?
var accounts = loadAccounts()!
This line is the culprit for you. Precisely this symbol ! is I guess. You are trying to fetch data from a database or a filesystem and expect it will always be there.
Think about it, it can't be true all the time.
# check if account array is not empty; I would have returned nil if it would be empty and so we can avoid that extra check here.
if let accounts = loadAccounts(), !accounts.isEmpty {
let tableController = AccountTableViewController()
self.window!.rootViewController = tableController
return true
}
let welcomeController = WelcomeViewController()
self.window!.rootViewController = welcomeController
Also, if you can provide more info about the error message from your debug console. Then I would be able to help in a better way.

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'.

Is is ok to have weak self in viewDidLoad

I am implementing this drop down menu from cocoaPod. It is pretty easy to implement and I got it to work.
https://github.com/PhamBaTho/BTNavigationDropdownMenu
However, as per instruction, I have implemented the following functions in viewDidLoad
self.navigationItem.titleView = menuView
menuView.didSelectItemAtIndexHandler = {[weak self] (indexPath: Int) -> () in
print("Did select item at index: \(indexPath)")
if indexPath == 0 {
print("Closest")
self?.sortByDistance()
} else if indexPath == 1 {
print("Popular")
self?.sortByRatings()
} else if indexPath == 2 {
print("My Posts")
self?.myPosts()
} else {
}
I am abit concerned as Xcode is telling me to put a ? or a ! just after self which was never done in other places of my program. Could someone please advise if this is totally acceptable or is there a better way of doing it? It just seems odd force unwrapping or putting my VC as optional...?
The whole point of {[weak self] ... is that the controller may be released and you don't want this block to strongly capture it and keep it in memory if it has been released by whatever presented it. As such the reference to self may be nil.
So, you definitely don't want to use !, and you should either user ? or and if let check.

instantiateViewControllerWithIdentifier is crashing (found nil)

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.