why does instantiateViewControllerWithIdentifier need to use "as!" - ios

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

Related

Don't have to cast segue destination view controller anymore?

I saw this in a codebase:
BlablaViewController *bbVC = segue.destinationViewController;
I thought this always needed to be casted to the correct type. Did something change in Objective-C recently that makes this cast no longer necessary?
since xcode 7 you have a new annotation called __kindof which allows you to point with UIViewController subclass (BlablaViewController in your case) to UIViewController. please see this example:
https://happyteamlabs.com/blog/how-to-use-__kindof-in-objective-c/

Navigate to UIViewController in Swift 2.3

I am trying to navigate to a UIViewController using Swift 2.3. To be more precise, I am trying to reload the UIViewController that is currently active. I do not know which view the user currently has active, so this must be defined dynamically.
I have tried several approaches, but they all result in either compile or runtime errors.
Is something like this possible?
let activeViewIdentifier = ??? // Get currently active view identifier as a string
self.performSegueWithIdentifier(activeViewIdentifier, sender:self)
You can get like this :
Objective-C :
self.navigationController.topViewController.restorationIdentifier
Swift :
self.navigationController?.topViewController?.restorationIdentifier
I think you have some issues with your architecture; it's not the best approach to reload just everything on some View Controller you can chose;
Much better way of thinking is to determine, what exactly you want to reload and add methods to reload only thus things
Anyway, if my answer hasn't assure you, consider replacing existing view controller with new and presenting it with some animation, or without it; so your general algorithm may look like this:
Get new VC from storyboard, or creating new instance, if you don't prefer to use it
Push it over your existing controller
Reload stack of navigation controller, in which you are now
you can try this
let activeViewIdentifier = self.navigationController?.childViewControllers[(self.navigationController?.childViewControllers.count)!-1]
You can use the restorationIdentifier, it's right above the Storyboard identifier and it's a UIViewController property.
let activityIdentifierStr = activeViewIdentifier?.restorationIdentifier
self.performSegueWithIdentifier(activityIdentifierStr!, sender:self)

Avoid hard coding storyboard IDs in 2 places

When I want to set an ID for a View Controller I go into the storyboard file, select the ViewController, then on the Identity inspector I type in the Storyboard ID as something like "theVCID":
When I want to use that View Controller in code I do something like:
UIViewController *myVC = [myStoryboard instantiateViewControllerWithIdentifier:#"theVCID"];
Is there a way I can just hardcode this ID string in one location instead of hard coding it inside storyboard and when I use it in code?
If I could get a variable in the storyboard XML, I could just get the hardcoded string variable inside the XML source, but I don't know how/where to set up a variable so that the storyboard XML can access it.
Unfortunately you are forced to duplicate the declaration.
I wouldn't recommend parsing the XML as you will be basing that on the assumption of an implementation detail. Technically, storyboards could change their data type to anything in the future and this would break your implementation.
The safest way to do this is to keep your storyboard identifiers in a single class (say StoryBoardCoordinationController) and simply reference the storyboard objects through this single interface. Hopefully you should never be in the situation where these values are changing often, and if they are you should definitely seek to do something about it:)

UIWindow in Xcode 6 templates

I have a simple question, why is the main (and only UIWindow) on all of the Apple templates in Xcode declared as a var, rather than a let.
var window: UIWindow?
As I understand it we should use let wherever possible, and especially where the instance won't change.
Also using let with objects still allows you to modify their properties (frame etc).
I'm having a little trouble understanding when to use let with object (class types).
I assumed something like CLLocationManager and UIWindow would be perfect examples of when to use let with objects, but Apple don't seem to use let in their code.
Properties defined with the let keyword must have a value by the time the object is initialized. The window isn't created during initialisation, so it can't be a let.
It's the same situation for view controllers - none of the outlets, or indeed the view properties, can be lets, because they don't exist at initialisation, but are created later when the view is required.
let properties don't really work well for UIKit and view controllers - by design, view controllers don't do a lot of work on initialisation, and you can only set up lets at that point. You are absolutely correct in that let properties would be great for things like a location manager or a managed object context, but as it turns out lazy var is often a better bet.
In my (limited, like everyone!) Swift experience, let properties are great for making immutable model classes, and local lets are the default for creating references in-code.
You can use let properties and define them right there, so for a location manager:
let locationManager = CLLocationManager()
Think more about what information you need to create whatever's going into the property. If it can be made from scratch without any context, do it like the example above. If it needs to have a delegate or any other properties passed in, you'll probably be creating or setting it after initialisation so a let isn't appropriate.
If you define window property as let, then UIKit framework will not be able to set window property of your app delegate when instantiating your default view controller from main storyboard.
So the answer is:
If you want to do everything in code manually, you can make window property to be defined with let. This way you must initialize it in init(...) method of your AppDelegate.
Otherwise, if you want to use storyboards and have them instantiated automatically with default view controller then you must define window as var and enjoy routines that apple perform for you.
It is ought to be initialized inside the application:didFinishLaunchingWithOptions: function. That way you can create different windows that suit your needs. For example:
The application may be being launched in the background to perform a task - no need to initialize a window here.
Application may be being launched by a notification - a different window object may be created.
Also, as pointed out by #Keenle, if you're using interface builder to shape your application, the window is created and assigned at runtime. In no way this could be done if the window property was constant.

Is passing two nil paramters to initWithNibName:bundle: method bad practice (i.e. unsafe or slower)?

According to a comment in UIViewController regarding initWithNibName:bundle:
If you invoke this method with a nil nib name, then this class' -loadView method will attempt to load a NIB whose name is the same as your view controller's class.
I always name my nibs the same as the view controller.
Is it a bad practice (i.e. unsafe, slower, or likely to cause problems down the road) to just pass nil to both parameters instead of a nib name string?
The main driving desire behind wanting to do this is I've found that using the refactor option in Xcode doesn't rename the nib name strings (only the class names wherever they're used). Thereby, this causes crashes if one isn't careful to go back and rename these everywhere.
It's not unsafe or slow. It just means that there will be an extra check to see that the NIB with the same name as your class really exists in the bundle, then it will be loaded exactly as if you had passed the NIB name. It won't cause problems unless you decide to change how you name your NIBs. In face, there's a whole discussion about how -initWithNibName:bundle: is a bad initializer, and just using -init is better.

Resources