Swift 4.1 - Subclass UIImage - swift

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.

Related

Why is this subclass failing to be archived by NSKeyedArchived?

I have this class that have to be the subclass of OIDAuthState (this class is an NSObject and conforms to NSSecureCoding),
The code to encode the class OIDAuthState was working fine before
self.data = try NSKeyedArchiver.archivedData(withRootObject: authState, requiringSecureCoding: true)
Once I created this subclass that includes adds a simple Boolean to the class isExpiredTokenEnforced, I now get the error when using NSKeyedArchiver,
The error says:
NSDebugDescription=Caught exception during archival: *** -decodeBoolForKey: only defined for abstract class. Define -[NSKeyedArchiver decodeBoolForKey:]!
Here is the subclass I am trying to archive and down below the override method to encode and decode this class, I am simply encoding and decoding this 1 extra property and then passing down the coder to the parent class,
class AuthenticationStateManager: OIDAuthState, AuthenticationState {
var isExpiredTokenEnforced = false
var lastTokenResponseInterface: TokenResponse? {
if isExpiredTokenEnforced {
return EnforcedExpiredTokenResponse(idToken: super.lastTokenResponse?.idToken,
accessToken: super.lastTokenResponse?.accessToken)
} else {
return super.lastTokenResponse
}
}
override static var supportsSecureCoding: Bool { true }
required override init(authorizationResponse: OIDAuthorizationResponse?, tokenResponse: OIDTokenResponse?, registrationResponse: OIDRegistrationResponse?) {
super.init(authorizationResponse: authorizationResponse, tokenResponse: tokenResponse, registrationResponse: registrationResponse)
}
required init?(coder: NSCoder) {
coder.encode(isExpiredTokenEnforced, forKey: "isExpiredTokenEnforced")
super.init(coder: coder)
}
override func encode(with coder: NSCoder) {
self.isExpiredTokenEnforced = coder.decodeBool(forKey: "isExpiredTokenEnforced")
super.encode(with: coder)
}
}
I can't figure out why this is failing I couldn't find relevant information to fix this,
does anyone have a clue what could be wrong here?
Thank you in advance for your time.
You are mixing up your encoder and decoder functions. Your func encode() is trying to call decodeBool. It should be calling encode().
Similarly your init?(coder:) function should be calling decode (or rather decodeBool(forKey:).)

How to override SKScene init(fileNamed: fileNamed)

I have a SKScene class in which I need to implement a custom initializer that override the initializer init(fileNamed: fileNamed) of the SKScene superclass SKNode, in order to do some proprietary initializations but keep the possibility offered by init(fileNamed: fileNamed) to load the scene from the interface builder.
I have some trouble to find the right syntax. I've tried the following:
class Try: SKScene
{
override init(fileNamed: String)
{
super.init(fileNamed: fileNamed)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
But it returns the error : Initializer does not override a designated initializer from its superclass
It is however a designated initializer from a superclass, not the immediate superclass which is SKEffectNode, but the one above which is SKNode. How can I override the initializer in this case ?
J.
From the SWIFT docs:
Conversely, if you write a subclass initializer that matches a superclass convenience initializer, that superclass convenience initializer can never be called directly by your subclass, as per the rules described above in Initializer Delegation for Class Types. Therefore, your subclass is not (strictly speaking) providing an override of the superclass initializer. As a result, you do not write the override modifier when providing a matching implementation of a superclass convenience initializer.
If I understand this right this code should work:
convenience init?(fileNamed filename: String)
{
self.init(fileNamed: filename)
}

Cannot override initialiser in subclass of generic class

I'd like to make a subclass of a generic class that is itself a subclass but I cannot override the initializer.
To clarify:
class BaseClass {
init(printMe: String) {
print(printMe)
}
}
class GenericClass<T>: BaseClass {
var object: T?
}
class NotGenericClass: GenericClass<NSNumber> {
override init(printMe: String) { // <- Error here
super.init(printMe: printMe)
// More setup
}
}
let a = NotGenericClass(printMe: "Print this")
a.object = NSNumber(int: 1)
The override init(printMe: String) line in NotGenericClass gives the following error:
Initializer does not override a designated initializer from its superclass
If GenericClass did not take a type parameter, this code would work. Is this a bug with Swift or am I misunderstanding how subclassing generic classes should work?
It seems that GenericClass doesn't automatically inherit the initializer in this case.
This makes it work:
class GenericClass<T>: BaseClass {
var object: T?
// Explicitly provide an initializer
override init(printMe: String) {
super.init(printMe: printMe)
}
}
This isn't clear from the Automatic Initializer Inheritance section of the docs, which claims that "If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers". You might want to file a bug.

Initialization of class hierarchy now fails in Swift 1.2

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.

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)
}