init(coder:) has not been implemented in swift - swift

I have this class file for accepting card payments
import UIKit
class PaymentViewController: UIViewController , PTKViewDelegate {
var card : STPCard
var PaymentView : PTKView
var button = UIButton.buttonWithType(UIButtonType.System) as UIButton
init(PaymentView : PTKView , button : UIButton, card : STPCard) {
self.PaymentView = PaymentView
self.button = button
self.card = card
super.init()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
When I build it, it works fine, but when I execute it (run it) on my actual device , I get
fatal error: init(coder:) has not been implemented.
Any ideas ?

Based on the Inheritance Hierarchy you have setup. PaymentViewController will inherit 3 init methods.
UIViewController provides init(nibName:bundle) as its designated initializer.
UIViewController also conforms to NSCoding which is where the required init(coder:) comes from.
UIViewController also inherits from NSObject which provides a basic init() method.
The problem you are having stems from init(coder: being called when the ViewController is instantiated from a .xib or storyboard. This method is called to un-archive the .xib/storyboard objects.
From the Documentation:
iOS initializes the new view controller by calling its initWithCoder: method instead.
You should be calling the superclass designated initializer in your init method, which is init(nibName:bundle) Note: it is fine for both of those parameters to be nil. Also your init(coder:) override should call super.init(coder:)

A simple workaround will do:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}

Related

NSWindowController designated initializer puzzle

I'm trying to make this code work:
class MyWindowController: NSWindowController
{
let thing: Thing
convenience init(thing: Thing)
{
self.thing = thing
super.init(windowNibName: NSNib.Name(rawValue: "MyNib"))
}
}
The problem, of course, is that a convenience initializer can't call init from a superclass. So how do I initialize my thing and still be able to call init(windowNibName:), which is itself a convenience initializer? I'd rather not have to re-implement the nib loading myself, but how do I avoid it if I can only use designated initializers?
According to the NSWindowController documentation:
You can also implement an NSWindowController subclass to avoid requiring client code to get the corresponding nib's filename and pass it to init(windowNibName:) or init(windowNibName:owner:) when instantiating the window controller. The best way to do this is to override windowNibName to return the nib's filename and instantiate the window controller by passing nil to init(window:). Using the init(window:) designated initializer simplifies compliance with Swift initializer requirements.
You can implement your class as:
class MyWindowController: NSWindowController
{
let thing: Thing
override var windowNibName: NSNib.Name? {
return NSNib.Name(rawValue: "MyNib")
}
init(thing: Thing) {
self.thing = thing
super.init(window: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Swift UIViewController crashing w/ (fatal error: init(coder:) has not been implemented)

I have created a swift file MainViewController that declares/defines a class MainViewController. I've set my storyboard's only scene's custom class as this class. I have the following code:
import Foundation
class MainViewController : UIViewController {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.view.backgroundColor = UIColor.blueColor()
}
}
When I run the project I get the following crash log:
fatal error: init(coder:) has not been implemented: file /Users/alexanderbollbach/Desktop/DraWave/content/MainViewController.swift, line 17
It seems that this is an issue people have experienced before and that swift has introduced some complexities regarding view controller initialization. I am currently learning swift but cannot resolve what the issue is. It may be worth noting that this is a mixed-language project that contains objective-c classes.

Idiomatic pattern to initialize UIViews in Swift

In Objective-C I had evolved the pattern of having both an awakeFromNib and initWithFrame: method which invoked their super's and then called a _commonInit where I put all my own code. E.g.
- (void)_commonInit {
// Initialize stuff here
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self _commonInit];
}
return self;
}
- (void)awakeFromNib {
[super awakeFromNib];
[self _commonInit];
}
So I'm trying to reuse this pattern in my UIView subclasses that I'm porting to Swift:
func _commonInit() {
// initialize code here
}
override init(frame:CGRect) {
super.init(frame:frame)
self._commonInit()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func awakeFromNib() {
super.awakeFromNib()
self._commonInit()
}
Is this the right way to do it? I'm curious why the init(coder...) is required. Especially when all I do is call the super version. I seem to recall that the reason I used awakeFromNib in the Objc version was because any changes applied from nib restoration didn't happen until sometime later than initFromCoder:.
Having a commonInit method that gets called from your initializers is a perfectly fine pattern for cases where you have more than one designated (or required) initializer. It's not the only pattern, though.
To address each of your doubts in turn (and add some related points)...
init(coder:) is required because you're subclassing a class that declares conformance to the NSCoding protocol. That protocol demands that all instances of all subclasses be able to initialize from an archive.
You don't actually have to do anything in init(coder:) unless you save state in encodeWithCoder(_:), though. But because it's possible for a subclass to have encodable state that's not inherited, initialization safety requires that the subclass be responsible for this initializer (even if all it does is call super).
You use awakeFromNib() in any custom class loaded from a nib/storyboard to take care of initialization that needs to happen only after an object's outlets and actions have been hooked up.
When Cocoa (Touch) loads a nib, it first initializes each object (with init(coder:)), then after all the objects are "live", it connects all the IBOutlet variables and the targets for all of the IBActions sent by controls. After all that's done, it calls awakeFromNib().
There's a caveat—a paradox, even—to using the commonInit pattern in Swift: You must initialize properties (aka instance variables) before calling super.init(), and you can't access self (including to call methods on self) until after calling super.init(). So you can't have a commonInit method that sets the initial values of properties.
You can have properties whose types are implicitly unwrapped optionals. Those are automatically initialized to nil, and then you can set a "real" initial value in your commonInit method:
var name: String!
init(frame: CGRect) {
super.init(frame: frame)
self.commonInit()
}
init(coder: NSCoder) {
super.init(coder: coder)
self.commonInit()
}
private func commonInit() {
name = // something...
}
An alternative pattern that gets around this issue is to supply initializers (or even lazy initializers) for each of your properties. If you do this, you don't need a commonInit method, because the property initializers will be implicitly called from whichever init actually gets used (or in the case of lazy initializers, when the property is first accessed).
class MyView: UIView {
let fileManager = NSFileManager.defaultManager()
let appDelegate = UIApplication.sharedApplication().delegate as! MyAppDelegate
lazy var name: String = { /* compute here */ }()
init(frame: CGRect) { super.init(frame: frame) }
init(coder: NSCoder) { super.init(coder: coder) }
// ...
}
Finally, if you do provide a commonInit (or similar) method, you don't need to mangle the name with an initial underscore or anything—Swift has built-in access control, so any methods you don't want exposed to callers outside of a class can simply be marked private.

Accessing function in MasterViewController from DetailViewController (swift)

I have a function in MasterViewController
func removeLocation(city: String){
objects.removeObject(city)
self.tableView.reloadData()
}
In my DetailViewController I check whether the city is valid and if its not, I want to remove it from the table in MasterViewController. I pass self in prepareForSegue() from MasterView to DetailView and I assign it to
var masterViewController: MasterViewController
But then I get an error saying that it is not initialized and it want me to have this initializer
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
Which breaks my whole program when I run it and gives me an expected fatal error.
Ho I can create an instance of MasterView in DetailView without an initializer, or access a function in MasterView from DetailView?
This is Swift btw, I found some advices on how to solve it in C but I couldn't implement them.
Use an optional.
var masterViewController: MasterViewController?
Optionals do not have to be initialized when the class is created.
In prepareForSegue() assign the pointer as usual:
destinationViewController.masterViewController = self
Then when you need to call removeLocation:
masterViewController?.removeLocation("London")

Instantiating and pushing view controller programmatically with custom initializer swift

I want to present a detail view controller in swift like so
let detailController = MyDetailUIViewController(nibName: "MyDetailUIViewController", bundle: NSBundle.mainBundle(), data:myData)
self.navigationController?.pushViewController(detailController, animated:true)
The issue I am having is how to write my initializer for MyDetailViewController:
class MyDetailUIViewController: UIViewController {
private var data: MyData
init(nibName nibNameOrNil: String!, bundle nibBundleOrNil: NSBundle!, data:MyData) {
self.data = data
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
I am getting an error that my data property is not initialized at super.init call. Can anyone explain how I can accomplish this? I am sure I could make my data property optional and pass that in after initialization, but surely there is a way to make this work.
If you declare your data variable to be non-optional, you have to make sure that all init methods initialize it. This is not the case in your class, since
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
does not initalize your data.
If your declare your initialize as convenience init() then you will not have to declare an implementation for init(coder:) this may solve your problem as well.