Swift has a very well thought-out initializer system in place. With options such as designated and convenience initializers, one must ensure all properties have values since the compiler will make sure of it. Take a look at my other post for more details.
The initialization rules are there for very good reasons, but sometimes it can make things tedious. For example, in the “UIViewController” class, there are 2 initializers you must handle since the view controller can be instantiated programmatically or through the storyboard. What this means is redundant code will be present like this:
class SomeViewController: UIViewController { private let videoPlayer: AVPlayer private let videoPlayerLayer: AVPlayerLayer override init(nibName: String?, bundle nibBundle: NSBundle?) { videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov")) videoPlayerLayer = AVPlayerLayer(player: videoPlayer) super.init(nibName: nibName, bundle: nibBundle) } required init?(coder decoder: NSCoder) { videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov")) videoPlayerLayer = AVPlayerLayer(player: videoPlayer) super.init(coder: decoder) } }
The compiler is requiring me to declare values for “videoPlayer” and “videoPlayerLayer” before calling “super.init“. It’s happening in two initializers. This is just a simple example, but you can imagine how the repeated code can get out of hand with more complex initialization needs.
Your first thought may be: why not wrap it in a function and call the function from both initializers. Nope. Can’t do that because you cannot reference “self” for the method call before calling “super.init“, and you can’t call the method after initialization either until you’ve initialized all properties – catch 22:
class SomeViewController: UIViewController { private let videoPlayer: AVPlayer private let videoPlayerLayer: AVPlayerLayer override init(nibName: String?, bundle nibBundle: NSBundle?) { // ERROR: Cannot use "self" before initialization, // but can't use method after initialization until // all properties declared - catch 22 commonInit() super.init(nibName: nibName, bundle: nibBundle) } required init?(coder decoder: NSCoder) { // ERROR: Cannot use "self" before initialization, // but can't use method after initialization until // all properties declared - catch 22 commonInit() super.init(coder: decoder) } func commonInit() { videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov")) videoPlayerLayer = AVPlayerLayer(player: videoPlayer) } }
So on the swift-evolution mailing list, I was trying to propose a new “defer init” keyword for this dilemma, something like this:
class SomeViewController: UIViewController { private let videoPlayer: AVPlayer private let videoPlayerLayer: AVPlayerLayer override init(nibName: String?, bundle nibBundle: NSBundle?) { super.init(nibName: nibName, bundle: nibBundle) } required init?(coder decoder: NSCoder) { super.init(coder: decoder) } defer init() { // New proposed keyword videoPlayer = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov")) videoPlayerLayer = AVPlayerLayer(player: player) } }
My thought was this would piggy-back off the “defer” keyword concept used for functions. However, someone chimed in and said “defer init” gives the wrong idea because it makes you think this new initialization keyword would be called after instead of before the super initializers, which I totally agree with so I dropped the idea.
Then something amazing happened. Someone else chimed and said this is how they do it:
class SomeViewController: UIViewController { private typealias My = SomeViewController private let videoPlayer: AVPlayer private let videoPlayerLayer: AVPlayerLayer override init(nibName: String?, bundle nibBundle: NSBundle?) { (videoPlayer, videoPlayerLayer) = My.commonInit() super.init(nibName: nibName, bundle: nibBundle) } required init?(coder decoder: NSCoder) { (videoPlayer, videoPlayerLayer) = My.commonInit() super.init(coder: decoder) } private static func commonInit() -> (AVPlayer, AVPlayerLayer) { let player = AVPlayer(URL: NSURL(fileReferenceLiteral: "movie.mov")) let layer = AVPlayerLayer(player: player) return (player,layer) } }
Wow, nice! Solves the redundant code problem while still abiding by the initialization rules. It’s using a static function to initialize the properties, which indeed can be called before the class is initialized (since it’s static and not using self).
Secondly, it’s returning a tuple to initialize multiple properties at once. That’s cool too! And for the sugar on top, it’s using a “typealias” like “My” or “I” to keep the static calls short.
This is just one of the many examples of why Swift is such a beautiful language, and also has a remarkable community to go with it. I encourage you to join the swift-evolution mailing list to contribute to the future course of Swift while learning new things from the community.
Happy Coding!!
*Note: In case you’re interested, here’s the thread mentioned here to see how it played out.
It’s look great, just wondering… I just start swift for couple months and stuggle with redundant code in init so I solve it by using
var a: UIView = {
return UIView()
}()
Is this making any difference?
Using self-executing closures for properties is great and I primarily use that pattern, thanks for mentioning it. You can even add “lazy” in front if you need to reference “self“. Perhaps I should’ve made the example in this post more complex, but this “commonInit” pattern would be an extra option if a closure wasn’t enough in some cases. See this great post about lazy executed closure properties: http://mikebuss.com/2014/06/22/lazy-initialization-swift/, although I do not agree with it in that you need “[unowned self]” since the closure is executed and released immediately after so no retain cycles. Mix it with shorthand argument names and you got another really cool pattern!
Hey @tpeodndy, I wanted to get back to about making the sample in this post a better example. I changed the variables to be immutable using the “let” keyword. That way, after initialization they cannot be changed.
This is the difference between lazy / self-executed properties, which are mutable and may not be what you want (you can change “a” in your example). I hope this clarifies and thanks again for the valuable comment!
If init’ing the UIView is all your closure is doing, you can rewrite your code more simply as:
let a = UIView()
Obviously if you’re doing other setup stuff for the view, then maybe the closure is necessary.
Doesn’t seem like self can be used inside static method. If it is so, this approach is definitely interesting, but limited.
Good point! In those cases you’d be stuck going with a mutable self-executing closure property instead.
Interesting. I like the way the tuple is used to feed back the initialize methods with instances.
If the class is simple enough to only need the player component, I would prefer the implementation below as it’s shorter and doesn’t contain calls to the same method in different places:
class SomeViewController : UIViewController {
class Player {
var player : AVPlayer
var layer : AVPlayerLayer
init(fileName:String) {
self.player = AVPlayer(URL: NSURL(fileReferenceLiteral: fileName))
self.layer = AVPlayerLayer(player: self.player)
}
}
private let player = Player(fileName:”movie.mov”)
}
Indeed that is preferred, though the static method would be helpful for subclasses that want to write their own initializers.
This is indeed a clever solution but a quick question though how would you go about doing it if we were to pass properties values to init instead of hard coding them. Also wouldnt it become clumsy if there were say 5 properties instead of two?
Sounds like `lazy var player: AVPlayer = {…}()` would be a better option in these cases. If you absolutely need the property to stay immutable, then perhaps static constants or feeding the static func with parameters would work.