Can't use concrete subclass to implement a property in protocol in Swift
I think the compiler error is misleading. DriverType
states that any class adopting it must expose a vehicle
property with Drivable
type, not a property with a class type adopting the Drivable
type.
I would solve this issue by defining both the DriverType
protocol and the Car
class using generics:
protocol Drivable {
var speed: Double { get }
init()
}
protocol DriverType {
typealias T: Drivable
var vehicle: T { get }
}
class Car: Drivable {
var speed = 80.0;
var brand = "BMW"
required init() {}
}
class Driver<T: Drivable>: DriverType {
var vehicle: T = T()
}
This explicitly states that classes adopting DriverType
must expose a vehicle
property whose type is any class adopting the Drivable
protocol.
Swift protocol conformance requirements for subtypes
When you define a variable in a protocol and assign a type to it, that is going to be a concrete type, so you cannot change it to a subclass of that type when conforming to the protocol. The type of the variable declared in the conforming class must be the same type as declared in the protocol, it cannot be a covariant (inheritance related) type.
You can fix the second error by creating an associatedType
for your Observer
protocol, which inherits from ObserverObject
then you can define object
to be of the same type as your associated type. Then you can make your ObserverClass
have a property object
of type ObservedObjectSubclass
.
class ObservedObject {}
class ObservedObjectSubclass: ObservedObject {}
protocol Observer {
associatedtype ObjectSubclass: ObservedObject
var object:ObjectSubclass { get }
}
class ObserverClass: Observer {
var object = ObservedObjectSubclass()
}
Add protocol to super class which will force other classes that inherit from it to implement protocol
What you are looking for is an abstract class. The purpose of an abstract class is to behave as a base class for concrete classes to inherit from, but an abstract class cannot be instantiated directly.
If Employee
was an an abstract class then any attempt to actually instantiate an instance of Employee
would be reported as an error by the compiler. You would need to instantiate a concrete subclass of Employee
, such as SalariedEmployee
or HourlyEmployee
.
The definition of the Employee
class would include that the calculatePay
method was required and again a compile time error would occur if a concrete subclass did not implement that method.
Now, the bad news. Neither Objective-C nor Swift supports abstract classes.
You can provide a similar kind of class by providing an implementation of a method that throws an exception if it isn't overridden by a subclass. This gives a runtime error rather than a compile time error.
e.g.
class Employee {
var givenName: String
var surname: String
...
init(givenName: String, surname: String) {
self.givenName = givenName
self.surname = surname
}
func calculatePay() -> Float {
fatalError("Subclasses must override calculatePay")
}
}
class SalariedEmployee: Employee {
var salary: Float
init(givenName: String, surname: String, annualSalary: Float) {
salary = annualSalary
super.init(givenName: givenName, surname: surname)
}
override func calculatePay() -> Float {
return salary/12 // Note: No call to super.calculatePay
}
}
Whether the calculatePay
is part of the base class or assigned to the base class through an extension that adds conformance to a protocol, the result is the same;
- The
Employee
class will need a default implementation of the function that generates some sort of error - Failure of a subclass to implement the method will not cause a compile time error
You could assign a protocol, say, Payable
to each subclass individually, but then as the protocol was not part of the base class, you couldn't say something like:
var employees[Employee]
for e in employees {
let pay = e.calculatePay()
}
You would have to use the slightly more complicated:
for e in employees {
if e is Payable {
let pay = e.calculatePay()
}
}
Swift - mixing abstract and concrete methods
I would implement it like this:
class AbstractAnimal
{
// Fully abstract method
func methodThatReturnsSomething() -> String {
fatalError("methodThatReturnsSomething() is abstract and must be overriden!");
}
func eyeCount() -> Int {
return 2;
}
}
fatalError
prevents Xcode from complaining that abstract method methodThatReturnsSomething()
doesn't actually return anything.
Swift: Abstract base class/protocol with private members
When I need an abstract base with some properties/functions hidden I use class with some additional fatalErrors
and asserts
to crash whenever someone is trying to use Base instead of implementation.
public class AbstractBase {
init() {
assert(type(of: self) != AbstractBase.self, "Abstract class")
}
fileprivate var _constant: Int {
fatalError("Abstract class")
}
fileprivate func _operation(_ val: Int) -> Int {
fatalError("Abstract class")
}
func mainOperation(_ val: Int) -> Int {
return _operation(val + _constant)
}
}
public class ConcreteSub: AbstractBase {
fileprivate override var _constant: Int {
return 42
}
fileprivate override func _operation(_ val: Int) -> Int {
return val + 2
}
}
Return object of concrete class that implements a protocol
Swift 4 ❤️
func createController(for identifier: String) -> (UIViewController & Protocol)
Swift protocols: Why does the compiler complain that my class doesn't conform to a protocol?
As you said yourself in the question, you can't just downcast to Bird
from Animal
. I propose changing the var to be optional, as an AnimalHouse
is likely to be without inhabitant some of the time.
In my implementation below non Bird
animals can't enter the birdhouse.
protocol AnimalHouse {
var myAnimal: Animal? {get set}
}
class Birdhouse: AnimalHouse {
var myAnimal: Animal? {
get{
return myBird
}
set(newanimal){
if let bird = newanimal as? Bird {
myBird = bird
}
}
}
private var myBird: Bird?
func isOpeningBigEnough() -> Bool {
return myBird?.wingspan <= 5.0
}
}
A further development of the AnimalHouse
protocol might be to add throws
to the setter (not possible as of Swift 2.0) or that an AnimalHouse
returns the type of animal it can house.
protocol AnimalHouse {
var myAnimal: Animal? {get set}
func houses() -> Any
}
class Birdhouse: AnimalHouse {
func houses() -> Any {
return Bird.self
}
}
ios swift class conforming protocol
Your issue is that while you can add protocol conformance via an extension, the extension is applied to the class, not an instance of that class. This means that you can say something like:
extension UITextField: ValidatesName {...}
But this will make all instances of UITextField conform to ValidatesName
Similarly, you could also say
extension UITextField: ValidatesEmail{...}
But now all instances of UITextField will conform to ValidatesName
and ValidatesEmail
.
Having separate Validates...
protocols doesn't seem like the right approach anyway. The basic signature of your protocol is something like var isValid: Bool
; this doesn't change between name and email. What does change is the validation logic and this has to live somewhere. This, coupled with the fact that you need subclasses in order to work with Interface Builder would suggest that a single protocol Validatable
that can be adopted by your various subclasses is a more reasonable approach.
protocol Validatable {
var isValid: Bool { get }
}
Now, you can define subclasses of UITextField that conform to this protocol (You can add the conformance via an extension to your subclass if you like, I just wanted to save space here)
class NameTextField: UITextField, Validatable {
var isValid: Bool {
get {
guard let text = self.text else {
return false
}
return !text.isEmpty
}
}
}
class EmailTextField: UITextField, Validatable {
var isValid: Bool {
get {
guard let text = self.text else {
return false
}
return text.contains("@")
}
}
}
Now, you can add your textfields to an array, and have something like:
@IBOutlet weak var nameTextField:NameTextField!
@IBOutlet weak var emailTextField:EmailTextField!
var validatableFields:[Validatable]!
override func viewDidLoad() {
super.viewDidLoad()
self.validatableFields = [nameTextField,emailTextField]
}
...
for field in validateableFields {
if !field.isValid() {
print("A field isn't valid")
}
}
How to declare a property of a particular class which is also protocol conformant?
Answering my own question 3 years later, Swift 4 supports combined class and protocol types:
let delegate: UIViewController & MyDelegateProtocol
Related Topics
Round Top Corners of a Uiview in Swift
(Swift) Nstimer Stop When Scrolling
Swift Realm, Load the Pre-Populated Database the Right Way
Include an Extension for a Class Only If iOS11 Is Available
How to Change the Uinavigationbar Title's Position
Timedmetadata' Deprecated. Another Method? <Updated>
How to Resolve This Build Issue - Cannot Assign to Property: 'Date' Is a Get Only Property
Key-Value Coding (Kvc) with Array/Dictionary in Swift
How Is a Type-Erased Generic Wrapper Implemented
What Is the Role of Avcapturedevicetype.Builtindualcamera
Detail View Is Not Updated When the Model Is Updated (Using List) Swiftui
How to Rotate Object in a Scene with Pan Gesture - Scenekit
Testing an Executable with Swift
Why Does Swift Return an Unexpected Pointer When Converting an Optional String into an Unsafepointer
Swiftui: Navigation Bar Title in Reusable Cross-Platform (iOS & MACos) View
Break on Any Occurrence of "Fatal Error: Unexpectedly Found Nil While Unwrapping an Optional Value"