Yedoye LaZar
About
-
Posted Answers
Answer
If you're not using storyboards and segues, then you are in charge of instantiating the view controllers of your project. While this requires a bit of additional work from your part, it has a few benefits. Because you are responsible for the initialization of each view controller, you can use initializer injection to inject the view controller's dependencies.
The example we explore in this episode is similar to the example of the previous episode. Download the starter project of this episode if you'd like to follow along. Notice that the main storyboard is missing. Each view controller of the project has a XIB file that defines its user interface.
The approach I recommend involves two steps. First, we define a designated initializer that accepts the dependencies of the view controller. Second, we set the view controller's dependencies in the initializer and invoke the designated initializer of its superclass.
Let's apply this technique to the RootViewController class. Before we implement the initializer, we declare the notes property as a constant and don't assign it an initial value. This implies that the value of the notes property needs to be set during initialization and that it cannot be modified once it's set. That is one of the advantages of using initializer injection.
The compiler throws an error. We can fix the error by defining a designated initializer for the RootViewController class that accepts an array of Note instances.
We assign the value of the notes parameter to the notes property. Once every stored property has a valid value, we can invoke the designated initializer of the superclass, init(nibName:bundle:).
The compiler doesn't agree with the changes we made. It throws another error. It notifies us that we need to implement a required initializer of the UIViewController class, init(coder:).
If we click the error, Xcode offers a solution. Click the "Fix" button to see what solution Xcode has in store for us.
This is what the implementation of the init(coder:) initializer looks like.
Because we defined a designated initializer, we are required to override the required initializers of the superclass, that is, the UIViewController class.
You may be wondering why Xcode adds an implementation of the init(coder:) method in which a fatal error is thrown. That sounds like asking for trouble.
To understand why Xcode offers this solution, we need to modify the implementation. Let's remove the fatal error and invoke the implementation of the superclass. This introduces another problem, though.
While this may appear to be a viable solution, it isn't. The compiler notifies us that the notes property doesn't have a valid value before we invoke the init(coder:) method of the superclass.
How do we set the value of the notes property? We can't pass a value for the notes property to the initializer because we need to implement the designated initializer as it's defined by the superclass. The answer is "We don't."
We won't be using the init(coder:) method and, if we want to use initializer injection, the only option we have is throwing a fatal error in the initializer. You may have seen this implementation before and wondered why a fatal error is thrown. This is the reason.
But it's the message of the fatalError(_:file:line:) function that confuses many developers. A better message would be "You should not instantiate this view controller by invoking init(coder:)." That makes more sense and explains why a fatal error is thrown.
Because the fatalError(_:file:line:) function doesn't return control to the initializer, the compiler is happy with this implementation. But remember that, if the init(coder:) method is invoked, the application is terminated. That's why it should never be invoked.
It's time to put the designated initializer to use. Open AppDelegate.swift and create an instance of the RootViewController class by invoking init(with:), passing in the array of note instances.
Because we pass the array of Note instances as an argument of the initializer, we don't need to configure the root view controller. We set the root view controller as the root view controller of the application's window.
Notice that we explicitly initialize the window of the application in application(_:didFinishLaunchingWithOptions:). Because we're not using a storyboard, we are responsible for initializing and configuring the window of the application.
Even though I like the conveniences storyboards offer, I prefer this implementation in the context of dependency injection. Why is that? As I mentioned earlier, we are responsible for instantiating the view controller. That means we can implement a custom initializer and pass the dependencies of the view controller during initialization. The result is that the dependencies can be declared as constants.
But there's another subtle benefit. The dependencies are defined by the initializer. The interface of the RootViewController class defines the dependencies of the view controller. Any developer inspecting the custom initializer of the RootViewController class understands that the class needs an array of Note instances to do its work.
The changes we need to make to the DetailViewController class are similar. We start by declaring the note property as a constant property. It no longer needs to be an optional, which means we don't need to rely on optional chaining in the viewDidLoad() method.
To instantiate the detail view controller, we need to invoke init(nibName:bundle:), passing in the name of the XIB file and the bundle in which the XIB file lives. We need to set the value of the note property before we do this. Remember that every stored property needs to have a valid value before the end of the initialization and before we invoke the initializer of the superclass.
Because we implemented a custom initializer, we are required to override the required initializer of the UIViewController class. We need to implement the init(coder:) method. Its implementation is identical to that of the RootViewController class.
Because we're not using segues to navigate the application, we don't need to implement the prepare(for:sender:) method in the RootViewController class. We need to present the detail view controller manually when the user taps a row in the table view. We do this in the tableView(_:didSelectRowAt:) method of the UITableViewDelegate protocol.
We safely unwrap the value that's returned by indexPathForSelectedRow and use the index path to fetch the Note instance that corresponds with the row the user tapped. We initialize an instance of the DetailViewController class and pass in the Note instance as an argument. We present the detail view controller modally by invoking present(_:animated:completion:).
Build and run the application to see if everything works as expected.
There's another benefit of using initializer injection over property injection that I'd like to point out. Because we pass the dependencies of the RootViewController and DetailViewController classes during initialization, we can declare the dependencies of these classes private.
Why is this important? A key benefit Swift has over Objective-C is powerful access control. It pays to embrace access control in Swift. Let me show you what I mean.
Open RootViewController.swift and choose Jump to Generated Interface from Xcode's Navigate menu. Xcode shows us the interface of the RootViewController class. This is similar to a header file in Objective-C.
Notice that there's no trace of the notes property since we declared it private. A developer new to the project sees that the initializer accepts an array of Note instances, but they don't know anything about the internals of the RootViewController class.
And that's how it should be. Other objects don't need to know how the RootViewController class manages the array of Note instances it's given during initialization. Ignorance is bliss and this very often applies to software development.
Answer is posted for the following question.
Answer
David Broadway Photography
Address: 10-14 Pier St, Perth WA 6000, Australia
Answer is posted for the following question.
Would you suggest best photographers in Perth, Australia?
Answer
Carnegie Mellon University
Address: Torrens Buidling, 220 Victoria Square, Adelaide SA 5000, Australia
Answer is posted for the following question.
What are the best universities in Adelaide, Australia australia?
Answer
Her Majesty's Theatre
Address: 58 Grote St, Adelaide SA 5000, Australia
Answer is posted for the following question.
Could you suggest best shows to see in Adelaide, Australia?
Answer
Gold Coast Fresh Meat Centre
Address: 64 Hutchinson St, Burleigh Heads QLD 4220, Australia
Answer is posted for the following question.
Where would I find where to buy best christmas ham in Gold Coast, Australia?
Answer
R* Why haven't I received the 200,000 GTA $ not taking the $200,000 that they were seemingly not going to give me despite advertisement
Answer is posted for the following question.
Why did gta give me 200k?
Answer
How do you pronounce szyszka in English? How to pronounce szyszka in English Use Youtube to practice the pronunciation of szyszka in real conversation
Answer is posted for the following question.
How to pronounce szyszka?