Initialization of class hierarchy now fails in Swift 1.2 - swift

Assuming the following class structure:
class A : SKSpriteNode
{
let foo : CGFloat
required init?(coder aDecoder: NSCoder) { ... }
init(foo: CGFloat)
{
self.foo = foo
super.init(
texture: SKTexture(imageNamed: "test"),
color: UIColor.blackColor(),
size: CGSizeMake(42, 42))
}
}
class B : A
{
required init?(coder aDecoder: NSCoder) { ... }
init()
{
super.init(foo: 42)
}
}
With A and B being in different modules, shows the following compiler error in B:
Initializer 'init()' overrides Objective-C method 'init' from superclass 'SKSpriteNode'
However, when changing init() in B to override init(), the following error occurs:
Initializer does not override a designated initializer from its superclass
Which is only solvable by adding init() in A which in turn calls the init of the base class.
Observations:
The above code did work in Swift 1.1 (i.e. without override).
It does work, when A and B are in the same module.
It does work, when overriding init() in A and using override init() in B.
The release notes state:
Imported C structs now have a default initializer in Swift, which initializes all of the struct's fields to zero.
But IMO this is not related, since SKSpriteNode is not a struct.
Is this a compiler bug or did I miss something else in the release notes?
Update Apple fixed this in Xcode 6.3 Beta 2.

Related

Swift 4.1 - Subclass UIImage

I get Overriding non-#objc declarations from extensions is not supported error when subclass UIImage with custom init after upgrading to Swift 4.1
class Foo: UIImage {
init(bar: String) { }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// Overriding non-#objc declarations from extensions is not supported
required convenience init(imageLiteralResourceName name: String) {
fatalError("init(imageLiteralResourceName:) has not been implemented")
}
}
Thanks for your help
extension UIImage {
/// Creates an instance initialized with the given resource name.
///
/// Do not call this initializer directly. Instead, initialize a variable or
/// constant using an image literal.
required public convenience init(imageLiteralResourceName name: String)
}
This init method is declared in the extension of the UIImage class.
The error pretty much says that if a function is declared in the extension than it can't be overridden in this way
class Foo: UIImage {
}
extension Foo {
convenience init(bar :String) {
self.init()
}
}
var temp = Foo(bar: "Hello")
You could try to achieve the desired result in this way.
The problem seems to be caused by the init(bar:) designed initializer, if you convert it to a convenience one, then the class will compile:
class Foo: UIImage {
convenience init(bar: String) { super.init() }
// no longer need to override the required initializers
}
Seems that once you add a designated initializer (aka a non-convenience one), Swift will also enforce overrides for all required initializers from the base class. And for UIImage we have one that lives in an extension (not sure how it got there, likely was auto-generated, as myself wasn't able to add a required initializer in an extension). And you run into the compiler error in discussion.

Creating a custom initalizer for SKScene that overrides convenience init?(fileNamed:)

I'm trying to create a convenience initializer that overrides the convenience init? (fileNamed:) initializer in SKScene so that I can pass some initial values to the scene while also unarchiving the .sks file. The problem is that when I try to do this, it seems that the subclass of SKScene (GameScene) is unable to see the convenience init? (fileNamed:) of the superclass. Here are some of my attempts:
Class GameScene : SKScene {
var stage : Int?
override init(size: CGSize) {
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init?(fileNamed: String, stage: Int) {
self.init(fileNamed: fileNamed) // Compiler error-- Argument labels '(filenamed:)' do not match any available overloads
self.stage = stage
}
Another attempt I found suggested as a workaround:
Class GameScene : SKScene {
var stage : Int?
override init(size: CGSize) {
super.init(size: size)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
convenience init?(fileNamed: String) {
self.init(fileNamed: fileNamed) // Error at run time: EXC_BAD_ACCESS (code=2, address=0x16fc1bff0)
}
convenience init?(fileNamed: String, stage: Int) {
self.init(fileNamed: fileNamed)
self.stage = stage
}
The debugger reveals an endless loop of GameScene.init(fileNamed : String) -> GameScene?
How do I accomplish this? I need to move on with my life!! (and this project...)
Couldn't it be as simple as this?
if let gameScene = GameScene(fileNamed: "GameScene") {
self.gameScene = gameScene
self.gameScene.stage = 1
self.gameScene.setupBasedOnStage()
self.gameScene.scaleMode = .aspectFill
self.gameScene.gameSceneDelegate = self.menuSceneDelegate as! GameSceneDelegate!
self.view?.presentScene(self.gameScene, transition: SKTransition.reveal(with: .down, duration: 1.0))
}
You are able to set the stage property before revealing the page, and if you needed to you can call a setup function to load info/graphics based on the stage.
I know it's not as elegant as what you are trying to do, but maybe sometimes the easiest answer is the best?
Swift has rules around 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.
The initializer public convenience init?(fileNamed filename: String) is itself a convenience initilizer on SKNode, so attempting to call it from your own convenience initializer breaks Rule 2.
If you see in the Xcode quick tips, the only initializer available to call from your convenience initializer would be required init?(coder aDecoder: NSCoder) which satisfies Rule 3.

Swift 1.2: 'self' used before super.init call

I have this code:
class SomeViewController : UIViewController {
let deferred : ()->()
required init(coder aDecoder : NSCoder) {
deferred = {
self.doSomething()
}
super.init(coder: aDecoder)
}
func doSomething() {
// Does things....
}
}
In Swift 1.2 this fails to compile with the error:
'self' used before super.init call
In pre-1.2 days, we can address this in a number of ways such as implicitly unwrapped conditionals. That approach won't work any longer.
I've seen other answers reference 2-stage initialization or the lazy decorator, but both sacrifice immutability of the property. Surely this must be solvable in Swift 1.2, but I'm out of ideas.
Here's an interim workaround:
private(set) var deferred : ()->() = { }
required init(coder aDecoder : NSCoder) {
super.init(coder: aDecoder)
self.deferred = {
self.doSomething()
}
}
My thinking is, okay, we did "sacrifice immutability of the property", but from a public perspective the property remains immutable because the setter is private.

Subclassing SCNScene in Swift - override init

I'm getting some compiler errors in Xcode 6 using Swift which I have a hard time wrapping my head around. I'm trying to create a scene by subclassing SCNScene, but keep getting errors on the initialisers. The basic structure of my code is:
class SpaceScene: SCNScene {
override init(named: String) {
super.init(named: named)
}
}
This results in an error on line 2 with the message "Initializer does not override a designated initializer from its superclass", although SCNScene clearly has such an initialiser. I think i'm missing something basic - any insights?
On XCode 6.1, the following should do it:
class SpaceScene : SCNScene {
override init() {
super.init()
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}

Override multiple overloaded init() methods in Swift

I'm attempting to create a custom NSTextFieldCell subclass in Swift (Xcode Beta 5), with the following code:
class CustomHighlightTextFieldCell : NSTextFieldCell {
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
init(textCell aString: String!) {
super.init(textCell: aString)
}
}
However, I receive compilation errors on the 2nd and 3rd init() declarations:
/Users/Craig/projects/.../CustomHighlightTextFieldCell:8:40: Invalid redeclaration of 'init(imageCell:)'
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:17:5: 'init(imageCell:)' previously declared here
/Users/Craig/projects/.../CustomHighlightTextFieldCell:7:39: Invalid redeclaration of 'init(textCell:)'
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:21:5: 'init(textCell:)' previously declared here
While there are some strange compiler bugs here (I'm getting the usual "SourceKitService Terminated, Editor functionality temporarily limited." message), it seems like I'm missing something in my method overriding - but I can't tell what.
I was under the assumption the named parameters, or at least the parameter types, would indicate that there are three different init() methods here, but apparently I'm missing a key piece of the puzzle.
Edit: If I add override to the 2nd and 3rd init() methods, I have a separate issue:
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
override init(textCell aString: String!) {
super.init(textCell: aString)
}
This new issue (simply invoked by adding the two override keywords) seems to almost contradict the original.
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:17:14: Initializer does not override a designated initializer from its superclass
/Users/Craig/projects/.../CustomHighlightTextFieldCell.swift:21:14: Initializer does not override a designated initializer from its superclass
It would seem that your declarations (with override) should be sufficient, however, they seem to need the #objc declarations as well. This works:
class CustomHighlightTextFieldCell : NSTextFieldCell {
required init(coder aCoder: NSCoder!) {
super.init(coder: aCoder)
}
#objc(initImageCell:)
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
#objc(initTextCell:)
override init(textCell aString: String!) {
super.init(textCell: aString)
}
}
As you see, you have the corresponding super.init(xxx) in the functions which means you are overriding the functions. So just add the override keyword.
override init(imageCell anImage: NSImage!) {
super.init(imageCell: anImage)
}
override init(textCell aString: String!) {
super.init(textCell: aString)
}