I've been working on an iOS application in Swift (much of it being moved from Objective-C). I'm using Core Data and trying to use extensions to add functionality to classes auto-generated from my model. One thing I readily did in Objective-C was to add a method in a category on class A and override that method in a category on class B (which derived from A), and I was hoping to do the same in Swift.
For a while now I've had the following code in my project (and this is just one example), and though I have not used the functionality yet, the compiler has worked just fine compiling this code:
// From CellType.swift -- NOTE: Imports from Foundation and CoreData @objc(CellType) class CellType: NSManagedObject { @NSManaged var maxUses: NSNumber @NSManaged var useCount: NSNumber // Other properties removed for brevity } // From SwitchCellType.swift -- NOTE: Imports from Foundation and CoreData @objc(SwitchCellType) class SwitchCellType: CellType { @NSManaged var targetCellXIndex: NSNumber @NSManaged var targetCellYIndex: NSNumber @NSManaged var targetCellType: CellType // Other properties removed for brevity } // From CellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData extension CellType { var typeLabel : String { get { return "Empty"; } } func isEqualToType(otherCellType : CellType) -> Bool { return (self.typeLabel == otherCellType.typeLabel && self.maxUses.isEqualToNumber(otherCellType.maxUses) && self.useCount.isEqualToNumber(otherCellType.useCount)); } // Code removed for brevity } // From SwitchCellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData extension SwitchCellType // YES, this compiles with the overrides! { override var typeLabel : String { get { return "Switch"; } } override func isEqualToType(otherCellType : CellType) -> Bool { var answer = false; if let otherSwitchCellType = otherCellType as? SwitchCellType { answer = super.isEqualToType(otherCellType) && self.targetCellXIndex.isEqualToNumber(otherSwitchCellType.targetCellXIndex) && self.targetCellYIndex.isEqualToNumber(otherSwitchCellType.targetCellYIndex) && self.targetCellType.isEqualToType(otherSwitchCellType.targetCellType); } return answer; } // Code removed for brevity }
Hopefully some kind Swift expert out there already sees my issue, but here's how I found out about it: Recently I tried to add similar functionality using methods that have parameters and/or return values that are not built in types, but I started getting this error: Declarations in extensions cannot override yet.
To explore this issue I added the following to one of my swift files, thinking it would compile just fine:
class A { } class B : A { } extension A { var y : String { get { return "YinA"; } } } extension B { override var y : String { get { return "YinB"; } } // Compiler error (see below) -- What?? }
To my surprise, I received the same compiler error (Declarations in extensions cannot override yet). What? But I've used that patter several times already without compiler errors.
Questions: First, are there certain rules about overriding in extensions such that in some cases it is supposed to work but in other cases it is not? Second (and more disconcerting) why does it seem that the Swift compiler is so inconsistent? What am I missing here? Please help me restore my faith in Swift.
UPDATE:
As noted in the correct answer by Martin R, it seems you can override methods in the current version of Swift (1.1 via Xcode 6.1) as long as they (1) involve only classes derived from NSObject and (2) do not use the inout modifier. Here's some examples:
class A : NSObject { } class B : A { } class SubNSObject : NSObject {} class NotSubbed {} enum SomeEnum { case c1, c2; } extension A { var y : String { get { return "YinA"; } } func f() -> A { return A(); } func g(val: SubNSObject, test: Bool = false) { } func h(val: NotSubbed, test: Bool = false) { } func j(val: SomeEnum) { } func k(val: SubNSObject, inout test: Bool) { } } extension B { // THESE OVERIDES DO COMPILE: override var y : String { get { return "YinB"; } } override func f() -> A { return A(); } override func g(val: SubNSObject, test: Bool) { } // THESE OVERIDES DO NOT COMPILE: //override func h(val: NotSubbed, test: Bool = false) { } //override func j(val: SomeEnum) { } //override func k(val: SubNSObject, inout test: Bool) { } }
It is not possible to override functionality (like properties or methods) in extensions as documented in Apple's Swift Guide. Extensions can add new functionality to a type, but they cannot override existing functionality. The compiler is allowing you to override in the extension for compatibility with Objective-C.
Extension methods cannot be overridden the way classes and instance methods are. They are overridden by a slight trick in how the compiler selects which extension method to use by using "closeness" of the method to the caller via namespaces.
It seems that overriding methods and properties in an extension works with the current Swift (Swift 1.1/Xcode 6.1) only for Objective-C compatible methods and properties.
If a class is derived from NSObject
then all its members are automatically available in Objective-C (if possible, see below). So with
class A : NSObject { }
your example code compiles and works as expected. Your Code Data extension overrides work because NSManagedObject
is a subclass of NSObject
.
Alternatively, you can use the @objc attribute for a method or property:
class A { } class B : A { } extension A { @objc var y : String { get { return "YinA" } } } extension B { @objc override var y : String { get { return "YinB" } } }
Methods which are not representable in Objective-C cannot be marked with @objc
and cannot be overridden in a subclass extension. That applies for example to methods having inout
parameters or parameters of an enum
type.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With