Subclassing view controller in Swift - ios

I want to subclass UIViewController and override init methods. I ended up with:
class BaseViewController : UIViewController, BaseViewCreating, ModelBinding {
var viewModel : Any!
convenience init(viewModel: Any){
self.init()
self.createUserInterface()
self.createConstraints()
self.viewModel = viewModel
self.bindWithModel(model: self.viewModel)
}
convenience init() {
self.init()
self.createUserInterface()
self.createConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func createUserInterface() {
}
func createConstraints() {
}
func bindWithModel(model: Any) {
}
func update(){
self.bindWithModel(model: self.viewModel)
}
}
However, when app launch, it throw an error - EXC_BAD_ACCESS on line self.init() in method convenience init().
How to fix that?

Thats because you are calling init recursively with self.init inside
convenience init() {
self.init()
}
Every single Convenience init should call designated initializer of the class. UIViewController has
public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?)
public init?(coder aDecoder: NSCoder)
as designated initializer. So you should call any one of them in your init() or override them in your ViewController and later call self.init(nibName or self.init(coder
As it cant find designated initializer by name init() it is calling recursively your init hence crashing
EDIT:
Just in case anybody has a doubt that self.init will not result in loop because thats one of the way how a Convenience initializer can call designated initializer. I have explained in detail why it results in infinite loop in this specific case in comment below :) attaching a proof here
This is the stack trace when I ran OP code. Clearly shows infinite recursive call

If anyone face same problem i paste answer here:
You should add this to your subclas:
init() {
super.init(nibName:nil, bundle:nil)
self.createUserInterface()
self.createConstraints()
}

Related

Assigning CLLocationCoordinate2D to blank instead of NIL

I am trying to assign default value for instead of fetching Nul. But I am getting an error:
Must call a designated initializer of the superclass 'UIViewController'
What should I do to eliminate the error?
Code:
var destination : CLLocationCoordinate2D
init(){
super.init()
self.destination.latitude = Double("")!
self.destination.longitude = Double("")!
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
The error suggests that this class is a UIViewController. The designated initializer for a UIViewController requires nibName & bundle. So when you call super.init without the arguments, it throws an error since the init method for UIViewController (which is the super class in this case) requires those arguments.
Basically, you have to replace super.init() with
super.init(nibName: String?, bundle: Bundle?)
A better approach:
Override the init method
var destination : CLLocationCoordinate2D
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
self.destination.latitude = Double("")!
self.destination.longitude = Double("")!
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
The right approach:
Don't use init at all. Instead, use UIViewController's lifecycle methods like viewDidLoad, viewWillAppear, & viewDidAppear

Swift 2.0 Subclassing a subclass of UIViewController and called convenience initializers

Have a bit of confusion regarding designated and convenience initializers for UIViewController in Swift 2.0/Xcode 7beta3. Our UIViewControllers are all defined in code, there are no Nibs
Currently class A inherits from UIViewController like this
class A : UIViewController {
convenience init() {
...
self.init(nibName:nil, bundle:nil)
...
}
}
Then class B inherits from class A and should override the convenience init and call its as super.init()
class B : A {
convenience init() {
super.init()
...
}
}
The compiler does not allow this with Must call a designated initializer of the superclass '...' error on super.init()
You need to make your initializers designated, not convenience:
class A : UIViewController {
init() {
super.init(nibName:nil, bundle:nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("")
}
}
class B : A {
override init() {
super.init()
}
required init?(coder aDecoder: NSCoder) {
fatalError("")
}
}
That gives you the inheritance structure you're looking for.
Take a look at these images found in the documentation.
(source: apple.com)
(source: apple.com)
According to the image convenience initializers are not inherited. So if you want to inherit you must make it a designated initializer.
class A : UIViewController {
init() {
super.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
class B : A {
override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
At this Point you may wonder what the heck is the difference between a designated and convenience initializer? Well, Convenience is used to call a designated initializer in the same class and you are suppose to use this to do some set up.
According to the document
Rule 2 A convenience initializer must call another initializer from the same class.
From https://developer.apple.com/library/mac/documentation/Swift/Conceptual/Swift_Programming_Language/Initialization.html(Initializer Delegation for Class Types)
You should call it's own initializer instead:
class B : A {
convenience init() {
init()
...
}
}
And init() is automatically inherit from it's superclass version
Here are rules how communicating designated initializers and convenience initializers
rule 1:
A designated initializer must call a designated initializer from its immediate superclass.
Rule 2:
A convenience initializer must call another initializer from the same class.
Rule 3:
A convenience initializer must ultimately call a designated initializer.
A simple way to remember this is:
Designated initializers must always delegate up.
Convenience initializers must always delegate across.

Swift 1.2: override init gives error [duplicate]

So I've just upgraded to Xcode 6.3 Beta 3 and a lot of error(s) are appearing relating to the following:
Initializer does not override a designated initializer from its superclass.
override init() {
super.init()
}
For example this is a UIButton class:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIButton'
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}
This is one of my UIViewController classes:
class CustomAlertView: UIViewController {
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
required override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIViewController'
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
My solution is a quick fix, but I think is easier than what Apple purposes on the the Release Notes. For more information search for 19775924 http://adcdownload.apple.com//Developer_Tools/Xcode_6.3_beta_3/Xcode_6.3_beta_3_Release_Notes.pdf here. What Apple says is that you create an Objective-C file and extend it (having to add it to the header files and all) and it's on "Known Issues in Xcode 6.3 beta 3", so I think is easy to do what I did:
This is how I fixed it for UIButton:
class CustomButton : UIButton {
init() {
super.init(frame: CGRectZero)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And this is one of my ViewControllers (remove public if not needed):
public class GenericViewController: UIViewController {
public init() {
super.init(nibName: nil, bundle: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I don't use IB so I also have UIView, because I do separate the view from the viewController (remove public if not needed):
public class GenericMenuView: UIView {
public init() {
super.init(frame: CGRectZero)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I need this specially in views because I have a setupViews method that I override in all subclasses that is called on the init. And using AutoLayout I don't need any frames (so I don't override the init with the frame parameter).
So it seems you have to drop override. Oh! and be sure to not call self.init() or the class is never initialized (and it crashes after some internal timeout).
As per Apple documentation here, what you are overriding is a convenience initializer. So for your initializer to work, you will have to change the method to
override convenience init() {
super.init()
}
You can either do that, or remove the initializer if you are not really using it except for calling the superclass initializer.
I recently figured this out and I'd like to explain what the problem was. Originally answered on the Apple Developer forums.
It seems Swift has changed the strategy for initializer dependency checking or for imporing initializers.
Now if your initializers' are as shown, one way to deal with both Xcode 6.3 Beta 2 and Beta 3 is to remove all initializer definitions:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
}
class CustomAlertView: UIViewController {
}
Without defining any designated initializers, classes inherit all initializers of their superclasses.
A pretty easy fix, but a big gotcha that had me stumped for a while.
I think this is way easier than it seems.
For an SKSpriteNode, I was doing this:
override init() {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
The problem is init() is not the designated initializer for SKSpriteNode. So I just changed it to:
override init(texture: SKTexture!, color: UIColor!, size: CGSize) {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
Now it works fine.
Solution for Error : Override init(coder aDecoder: NSCoder!) not working like expected - Swift
This works for me , Try this, Note: u must awake nib
override func awakeFromNib() {
super.awakeFromNib()
// Initialisation code
}

Initializer does not override a designated initializer from its superclass

So I've just upgraded to Xcode 6.3 Beta 3 and a lot of error(s) are appearing relating to the following:
Initializer does not override a designated initializer from its superclass.
override init() {
super.init()
}
For example this is a UIButton class:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIButton'
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
}
This is one of my UIViewController classes:
class CustomAlertView: UIViewController {
required init(coder aDecoder: NSCoder) {
fatalError("NSCoding not supported")
}
required override init() { // Initializer does not override a designated initializer from its superclass
super.init() // Must call a designated initializer of the superclass 'UIViewController'
}
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
}
My solution is a quick fix, but I think is easier than what Apple purposes on the the Release Notes. For more information search for 19775924 http://adcdownload.apple.com//Developer_Tools/Xcode_6.3_beta_3/Xcode_6.3_beta_3_Release_Notes.pdf here. What Apple says is that you create an Objective-C file and extend it (having to add it to the header files and all) and it's on "Known Issues in Xcode 6.3 beta 3", so I think is easy to do what I did:
This is how I fixed it for UIButton:
class CustomButton : UIButton {
init() {
super.init(frame: CGRectZero)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
And this is one of my ViewControllers (remove public if not needed):
public class GenericViewController: UIViewController {
public init() {
super.init(nibName: nil, bundle: nil)
}
required public init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I don't use IB so I also have UIView, because I do separate the view from the viewController (remove public if not needed):
public class GenericMenuView: UIView {
public init() {
super.init(frame: CGRectZero)
}
public required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I need this specially in views because I have a setupViews method that I override in all subclasses that is called on the init. And using AutoLayout I don't need any frames (so I don't override the init with the frame parameter).
So it seems you have to drop override. Oh! and be sure to not call self.init() or the class is never initialized (and it crashes after some internal timeout).
As per Apple documentation here, what you are overriding is a convenience initializer. So for your initializer to work, you will have to change the method to
override convenience init() {
super.init()
}
You can either do that, or remove the initializer if you are not really using it except for calling the superclass initializer.
I recently figured this out and I'd like to explain what the problem was. Originally answered on the Apple Developer forums.
It seems Swift has changed the strategy for initializer dependency checking or for imporing initializers.
Now if your initializers' are as shown, one way to deal with both Xcode 6.3 Beta 2 and Beta 3 is to remove all initializer definitions:
class CustomButton: UIButton {
var target: AnyObject!
var selector: Selector!
var action: (() -> Void)!
}
class CustomAlertView: UIViewController {
}
Without defining any designated initializers, classes inherit all initializers of their superclasses.
A pretty easy fix, but a big gotcha that had me stumped for a while.
I think this is way easier than it seems.
For an SKSpriteNode, I was doing this:
override init() {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
The problem is init() is not the designated initializer for SKSpriteNode. So I just changed it to:
override init(texture: SKTexture!, color: UIColor!, size: CGSize) {
let texture = SKTexture(imageNamed: "bgTile")
super.init(texture: texture, color: nil, size: texture.size())
}
Now it works fine.
Solution for Error : Override init(coder aDecoder: NSCoder!) not working like expected - Swift
This works for me , Try this, Note: u must awake nib
override func awakeFromNib() {
super.awakeFromNib()
// Initialisation code
}

Fatal error: use of unimplemented initializer 'init(coder:)' for class

I decided to continue my remaining project with Swift. When I add the custom class (subclass of UIViewcontroller) to my storyboard view controller and load the project, the app crashes suddenly with the following error:
fatal error: use of unimplemented initializer 'init(coder:)' for class
This is a code:
import UIKit
class TestViewController: UIViewController {
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// Custom initialization
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// #pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue?, sender: AnyObject?) {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
}
Please suggest something
Issue
This is caused by the absence of the initializer init?(coder aDecoder: NSCoder) on the target UIViewController. That method is required because instantiating a UIViewController from a UIStoryboard calls it.
To see how we initialize a UIViewController from a UIStoryboard, please take a look here
Why is this not a problem with Objective-C?
Because Objective-C automatically inherits all the required UIViewController initializers.
Why doesn't Swift automatically inherit the initializers?
Swift by default does not inherit the initializers due to safety. But it will inherit all the initializers from the superclass if all the properties have a value (or optional) and the subclass has not defined any designated initializers.
Solution
1. First method
Manually implementing init?(coder aDecoder: NSCoder) on the target UIViewController
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
2. Second method
Removing init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) on your target UIViewController will inherit all of the required initializers from the superclass as Dave Wood pointed on his answer below
Another option besides #3r1d's is to instead remove the following init method from your class:
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
// Custom initialization
}
Including that init method, prevents the sub class from inheriting the init(coder aDecoder: NSCoder!) from its super class. By not including it, your class will inherit both.
Note: See WWDC 2014 Session 403 "Intermediate Swift" at about the 33:50 mark for more details.
For people having the same issue with swift UICollectionViewCells, add the code that #3r1d suggested to your custom UICollectionViewCell class and not to the View Controller:
init(coder aDecoder: NSCoder!)
{
super.init(coder: aDecoder)
}
For those needing the code in Swift:
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
[Edit] This was for an older version of Swift. Possibly doesn't work anymore.
I had this problem in a programmatic collectionView cell and even though the op is asking about a vc I still landed on this question when searching for an answer. For me the issue was I did have
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
implemented so the top answer didn't work. What I didn't have in the cell was the initializer:
// my programmatic cell was missing this
override init(frame: CGRect) {
super.init(frame: frame)
}
Once I added it the error went away
Rather than adding some methods for making internal mechanism work fine, i would go with defining my attributes as #lazy and initialise them right in the class scope.

Resources