How to refer to navigation controller in Swift? - ios

I have created a UINavigationController object and set it as the window's rootViewController property.
The rootViewController of the UINavigationController object is a class called UINavigationMenuViewController.
If I want to navigate from UINavigationMenuViewController to UIUserProfileViewController for example, I can use:
navigationController!.pushViewController(userProfileVC, animated: true)
as well as
navigationController?.pushViewController(userProfileVC, animated: true)
The effect seems to be the same. I am wondering what's the difference.
I would guess the second way is more secure, and in case I forget to embed the UINavigationMenuViewController object inside the UINavigationController, the app would not crash, comparing to the first case. I guess it's also called optionals chaining, I am just not quite sure as I am still learning Swift.
Please give me some advice.

In case of doubt, it's always safer favoring optional chaining rather than forced unwrapping, for the reason you mentioned: if the variable is nil, it will cause the app to crash.
There are some cases where a crash is a good debugging tool though. If having your navigation controller set to nil, you might want to consider that as a development mistake, so making the app crash would make the mistake more explicit.
Besides that, my recommendation is to always use optional chaining and/or optional binding, and limit usage of forced unwrapping to cases where:
you are sure an optional is not nil
you have just checked for not nil
(as mentioned above) you do want the app to crash if the optional is nil

In the first case you unwrap navigationController explicitly so navigationController IS a UINavigationMenuViewController type and has to exist (or else crash). In the second case navigationController is an optional type and doesn't have to exist. If it doesn't exist of course nothing will happen and no view will be presented.

Related

Is init?(aDecoder:) or prepareForSegue(_:sender:) called first?

Intro
I am trying to debug my program and I have come across a problem. I am getting the swift error: Unexpectedly found nil while unwrapping an optional value. This problem is from an implicitly unwrapped optional. The error is in ViewControllerB's init?(aDecoder:) and the place where I set the value is in ViewControllerA's prepareForSegue(_:sender:) that uses a "show segue" to go to ViewControllerB.
Problem
This leads me to beleive that the init?(aDecoder:) method in ViewControllerB is called before the prepareForSegue(_:sender:) in ViewControllerA. Am I right?
Yes. An object is always initialised before it can be used elsewhere in the application.
If there is work you need to do that depends upon the property being set, it's often appropriate to trigger that work from a didSet observer on the property or in the viewDidLoad() method.

why does instantiateViewControllerWithIdentifier need to use "as!"

let someVC = self.storyboard!.instantiateViewControllerWithIdentifier("something") as! SomethingViewController
why do we need to use "as! DataEntryViewController"
it works when I take it out using xcode 7.3.1
Define "works".
Yes, you can instantiate a view controller without caring which subclass of UIViewController it is. And the object you get back from that method will be a view controller, so it's safe to do things with it that can be done to all view controllers: present it modally, push it onto a navigation controller, add it to a tab controller, etc.
However, if you're going to do something with it that's specific to your view controller class -- like if SomethingViewController defines a property that lets you choose which Something it displays, and you want to assign to that property -- you need to do two things:
Test at runtime that the object you got back from instantiateViewControllerWithIdentifier is the class you expect it to be. (Because depending on the identifier you pass it could be some other class, or nothing at all if that identifier isn't in the storyboard.)
Let the compiler know that the variable you've assigned that object to is typed for that class, so that the compiler will let you access properties and call methods of that class.
Assignment with an as? or as! cast does both of those things in one step. (Whether to use as? or as! just depends on how much you trust yourself to make sure your storyboard identifiers are what they claim to be, and how you want to handle failure of such assumptions.)
In fact, even if you're not using properties or methods specific to that view controller class, that as! cast adds a runtime check that your view controller class is what you expect it to be. So the fact that nothing breaks when you take the cast out isn't a sign that the cast is superfluous — it's a sign that whatever breakage the cast was checking for is not currently happening.
That is, the line you quoted would lead to a crash if somebody changed your storyboard so that the identifier "something" was on a SomethingElseViewController. If you took that cast out, you wouldn't crash there. (And you'd probably run into trouble later.)
However, if what you really want to do is assert the validity of your storyboard and program, it might be better to be clear about that:
let someVC = self.storyboard!.instantiateViewControllerWithIdentifier("something")
assert(someVC is SomethingViewController)
// then do something non-SomethingViewController-specific with it

Objective-C method execution while object is deallocated - should I fix that?

In methodA of my view controller VC1 another view controller's (VC2) method methodB is called in which VC1 is deallocated. Then control returns to VC1 methodA which obviously crashes when self is used.
It is not obvious that the instance is deallocated, so developers may use self without knowing that they should not. From that perspective I'd like to fix the deallocation. However, I'd like to get some opinions whether or not such a situation is common or needs to be fixed ASAP a.s.o.
Q1: Is execution of deallocated object's methods somewhat common which one will encounter in typical medium projects?
Q2: Would it be acceptable, if comments are added, which warn the developer?
Q3: Are there any other recommendations / opinions?
The project is of medium complexity (about 200 classes of which 50 are view controllers). I'd like to get a feeling about how much effort I should invest to take care of such "deallocated method calls". If it would be one of my smaller pet projects, it would be rather easy to fix anything. However, with an inherited project which already went through a couple of hands, things are not so trivial any more.
EDIT:
didFinish delegate scenarios:
Thinking about it, I came across didFinish kind of delegate calls. Assume a master view controller (MasterVC) uses a slave view controller (SlaveVC) to do some work, keeps a strong reference to it and sets itself as a delegate for the SlaveVC. When the slave is finished, it calls slaveVcDidFinish. In MasterVC's implementation of slaveVcDidFinish the strong reference to the SlaveVC is set to nil. So when the slaveVcDidFinish returns, it is SlaveVC's responsibility to never use self, as it may have already been deallocated.
- (void) notifyDelegate
{
[self.delegate slaveVcDidFinish];
// From here on, `self` may be invalid...
}
This likely is relevant, when the SlaveVC is dismissed before the call to slaveVcDidFinish, as otherwise it cannot be deallocated because it is part of the view controller hierarchy.
Q4: Is my understanding of this didFinish scenario correct?
[/EDIT]
A few details, in case it is relevant:
VC2 presents VC1 and keeps a strong reference
VC1 does its work and needs to present VC3 for which it dismisses itself before
VC3 does its work and delegates back to VC1's methodA
VC1's methodA delegates to VC2's methodB
methodB releases the strong reference to VC1, VC1 now gets deallocated (dealloc is called), and control returns to methodA
methodA does a little more work and then returns
One way to do the little more work was to call methods on an object passed into methodA as parameter. That should work, as self does not play a role there.
Another way to do that work was to call a method of VC1 using self which obviously causes a crash. So as long as one does not use self, everything should be fine.
Yes, you should fix it, but based on what you are saying your app architecture must be to a greater or lesser extent wrong. You are trying to implement an algorithm distributed across view controllers that really needs an object that lives across the lifetime of the objects you are dealing with. Such needs a mediator object.
Actually I suspect you may not need to be implementing that algorithm at all and another approach that works better with how controllers are instantiated and how control gets passed around the app would work fine, but you haven't posted sufficient info for me to be able to advise on that.
However having said that, implementing an overarching controller could certainly be used to solve your problem. A good way to do this is often to subclass your current root view controller (or better still a subclass that implements a category you define). So for example, if it is a UINavigationController you are using, create UINavigationController subclass, change the class of it's representation in your Storyboard (assuming you are using storyboards) and do the work of coordinating display and dismissal of other view controllers in there.
Also check out the Mediator design pattern http://cocoapatterns.com/ios-view-controller-transitions-mediator-pattern/
As a note, generally these days you can avoid having any object properties for strongly holding view controllers. Usually the root view controller holds strong references to any view controller it needs to present, lazy invocation should be used to present view controllers that are not currently presented or stored in an existing navigation controller stack (which keeps the memory utilisation profile in good shape) and any other child view controllers can be added in to your view controller heirarchy using the addChildViewController: method in which case they are strongly held in the childViewControllers array property.
You may want a convenient name for a view controller, but actually I would recommend writing a small bit of code for as the implementation of any such property that will identify the controller you need from amongst those in the Apple supplied properties, and dynamically retrieve it. This may seem like hard work, but it's worth it and actually, paradoxically, decreases code complexity and helps ensure you stay working with the view controllers the way Apple intended. Doing anything else increases pain.
Using separate object properties for holding references to view controllers will usually only duplicate mechanisms the existing Apple classes already provide you with. Such unnecessary duplication increases code complexity and can introduce bugs and memory management issues (of the kind you are in fact describing).
Sorry this answer is quite general, but you are seeking an answer to the wrong question. From what you have said it's clear that at some level you need to address your app architecture.
I agree the architecture seems flawed but as you say it is inherited code and not trivially small maybe some compromises must be made.
I am thinking since self (or any instance variables or properties) cannot be called anyway perhaps you could make the methods you need to call class methods (+) rather than instance methods (-). Class methods are obviously safe to call without an object instance.

Swift: unexpectedly found nil while unwrapping an Optional value when setting delegate for UITextField object

Update: with 2 downvote of this question, I'd like to make this question a little bit useful to others - since I don't have the choice to delete it. The mistake I made was cut and paste codes which has interface outlet. As I was completely new at that time, I was assuming that when I copy and paste, the outlet link will be copied and pasted as well. Obviously it doesn't work that way.
I was writing a single-viewed app. It has one UITextField and one MKMapView. I want to do something when Return key is hit, so I basically followed
How to hide keyboard in swift on pressing return key?
But it does not fit well with my other codes. Any idea why it isn't working and how to fix it?
Make sure you connect your UITextField from StoryBoard to your searchText IBOutlet by control-dragging from the StoryBoard to the the searchText variable.
You have your outlet set up as implicitly unwrapped. That's the correct thing to do, but when your code executes the outlet must not be nil or your code will crash.
You probabably have a broken outlet link. Set a breakpoint and examine the outlet.
You can change your code to use an "if let" expression to prevent crashes. Search in the Swift language reference IBook for "Optional binding" to learn about it.
Edit:
The code might look like this:
if let requiredSerachText = searchtext
{
requiredSearchText.delegate = self
}

Clean way to force view to load subviews early

Recently I wrote some code where I tried to refer to an outlet on a UIViewController I'd just instantiated with [storyboard instantiateViewControllerWithIdentifier] and modify the subview that the outlet pointed to before presenting the ViewController. It didn't work because the ViewController's view hadn't loaded its subviews yet, including the one that my outlet referred to, so the property just gave me a null pointer.
After (with some struggle) tracking down the cause of my issue in the debugger, I Googled around and learned, through answers like this one, that I can cause the view to load its subviews without being displayed by calling the myViewController.view getter. After that, I can access my outlet without any problems.
It's a clear hack, though, and Xcode - quite rightly - doesn't like it, and angrily protests with this warning:
Property access result unused - getters should not be used for side effects
Is there a non-hacky alternative way to do this that doesn't involved abusing the .view getter? Alternatively, are there canonical/idiomatic patterns for this scenario involving something like dynamically adding a handler to be called as soon as the subviews are loaded?
Or is the standard solution just to replace myViewController.view with [myViewController view] to shut up Xcode's warning, and then live with the hack?
On iOS 9 or newer, one can use:
viewController.loadViewIfNeeded()
Docs: https://developer.apple.com/reference/uikit/uiviewcontroller/1621446-loadviewifneeded
I agree that forcing a view to load should be avoided but I ran into a case where it seemed the only reasonable solution to a problem (popping a UINavigationController containing a UISearchController that had yet to be invoked causes a nasty console says warning).
What I did was use new iOS9 API loadViewIfNeeded and for pre-iOS9 used viewController.view.alpha = 1.0. Of course a good comment above this code will prevent you (or someone else) removing this code later thinking it is unneeded.
The fact that Apple is now providing this API signals it can be needed from time to time.
Not sure how much cleaner this way, but it still works fine:
_ = vc.view
UPD: for your convenience, you can declare extension like below:
extension UIViewController {
func preloadView() {
let _ = view
}
}
You can read explaination by following URL: https://www.natashatherobot.com/ios-testing-view-controllers-swift/
merged Rudolph/Swany answers for pre ios9 deployment targets
if #available(iOS 9.0, *) {
loadViewIfNeeded()
}
else {
// _ = self.view works but some Swift compiler genius could optimize what seems like a noop out
// hence this perversion from this recipe http://stackoverflow.com/questions/17279604/clean-way-to-force-view-to-load-subviews-early
view.alpha = 1
}
If I understand you correctly, I think there's another fairly standard solution: move the outlet modification/configuration code into a viewDidLoad method (of the recently instantiated VC).
The topic is also discussed in this question.
It would require some restructuring, but it might give you a "cleaner" design in terms of MVC if your incoming VC handled its own configuration, and it would avoid the "You should never call this method directly" stricture on loadView.
You can call [myViewController loadView] to explicitly load the view, instead of abusing the .view getter. The .view getter actually calls loadView if necessary when called.
It's still not a very nice solution, since the UIView Documentation's section on loadView explicitly instructs that
You should never call this method directly

Resources