How to override instance method from protocol extension in Swift? [duplicate] - ios

This question already has an answer here:
Implement protocol through extension [duplicate]
(1 answer)
Closed 6 years ago.
I'm trying to override an instance method from a protocol extension, and I'm having some trouble.
For context, I'm making an iOS app with a lot of different UICollectionViews. These views get data from different databases (requiring different callback funcs) and have very different layouts. Because any combination of (database, layout) is possible, it's difficult to make a nice OOP class hierarchy without massive code duplication.
I had the idea to put the layout functions (mostly those defined in the UICollectionViewDelegateFlowLayout protocol) into protocol extensions, so I can decorate a given UICollectionView subclass with a protocol that's extended to implement all relevant layout functions, but I'm having a hard time of it. The essence of the problem is contained in the code below.
class Base {
func speak(){
print("Base")
}
}
class SubA: Base, ProtocolA {}
class SubB: Base, MyProtocolB {}
protocol MyProtocolA{
func speak()
}
protocol MyProtocolB{
func speak()
}
extension MyProtocolA{
func speak(){
print("A")
}
}
extension MyProtocolA{
func speak(){
print("B")
}
}
let suba = SubA()
suba.speak() // prints "Base", I want it to print "A"
let subb = SubB()
subb.speak() // prints "Base", I want it to print "B"
Thoughts?

The default implementations in the protocols are only called if the class that conforms to these protocols do not implement that method itself. The classes' methods override the default implementations of the protocols, not the other way around.
Typically, you'd do something like:
protocol MyProtocolA {
func speak()
}
protocol MyProtocolB {
func speak()
}
extension MyProtocolA {
func speak() {
print("A")
}
}
extension MyProtocolB {
func speak() {
print("B")
}
}
class SubA: MyProtocolA {}
class SubB: MyProtocolB {}
let suba = SubA()
suba.speak() // prints "A"
let subb = SubB()
subb.speak() // prints "B"
But if you do
class SubC: MyProtocolA {
func speak (){
print("C")
}
}
let subc = SubC()
subc.speak() // prints "C"
Frankly, as you look at this, the use of Base is entirely redundant in this example, so I've removed it. Clearly, if you need to subclass from Base for other reasons, feel free. But the key point is that protocol default implementations don't override the classes' implementation, but rather the other way around.

Related

Trouble with Swift Protocols, associatedtypes, Self and default implementations

I am trying to get some functionality through default implementations that I can't nail. Consider the following code, which is a simplification of what I'm trying to do, but captures the problem as simply as possible.
//protocol definition
protocol Configurable {
associatedtype Data
func configure(data: Data)
static func generateObject() -> Self
}
//default implementation for any UIView
extension Configurable where Self: UIView {
static func generateObject() -> Self {
return Self()
}
}
//implement protocol for UILabels
extension UILabel: Configurable {
typealias Data = Int
func configure(data: Int) {
label.text = "\(data)"
}
}
//use the protocol
let label = UILabel.generateObject()
label.configure(data: 5)
print(label.text!) //5
I have a protocol, a default implementation for some methods for UIView, and the a specific implementation for UILabel.
My issue is the last part... the actual use of all this functionality
let label = UILabel.generateObject()
label.configure(data: 5)
print(label.text!) //5
I find myself doing generateObject() followed by configure(data: <something>) constantly. So I tried doing the following:
Add static func generateObjectAndConfigure(data: Data) -> Self to the protocol. The issue comes when I try to make a default implementation for UIView for this method. I get the following error
Method 'generateObjectAndConfigure(data:)' in non-final class 'UILabel' cannot be implemented in a protocol extension because it returnsSelfand has associated type requirements
Basically, I can't have a method that returns Self and uses an associated type. It feels really nasty for me to always call the two methods in a row. I want to only declare configure(Data) for each class and get generateObjectAndConfigure(Data) for free.
Any suggestions?
You're overcomplicating this a bit, by using Self.
All you need to do is declare an initialiser in your Configurable protocol that accepts your Data associatedtype as an argument, and has a non-static configure function:
protocol Configurable {
associatedtype Data
init(data: Data)
func configure(data: Data)
}
Provide a default implementation of that initializer in an extension for the Configurable protocol (for UIView and its subclasses):
extension Configurable where Self: UIView {
init(data: Data) {
self.init(frame: CGRect.zero)
self.configure(data: data)
}
}
Finally, add conformance to the protocol via an extension to any UIView subclasses you're interested in. All you need to do here is to implement the typealias and configure method:
extension UILabel: Configurable {
typealias Data = Int
func configure(data: Data) {
text = "\(data)"
}
}
extension UIImageView: Configurable {
typealias Data = String
func configure(data: Data) {
image = UIImage(named: data)
}
}
This implementation has the added bonus that you're using an initializer to create your views (the standard Swift pattern for instantiating an object), rather than a static method:
let label = UILabel(data: 10)
let imageView = UIImageView(data: "screenshot")
It's not exactly clear to me why the compiler doesn't like your version. I would have thought that subclasses of UILabel would inherit the typealias meaning that the compiler shouldn't have a problem inferring both Self and Data, but apparently this isn't supported yet.
Edit: #Cristik makes a good point about UICollectionView in the comments.
This problem can be solved by adding a protocol extension for Configurable where the Self is UICollectionView, using the appropriate initializer:
extension Configurable where Self: UICollectionView {
init(data: Data) {
self.init(frame: CGRect.zero, collectionViewLayout: UICollectionViewFlowLayout())
self.configure(data: data)
}
}
Then, when adding conformance to Configurable for UICollectionView, we make the Data typealias a UICollectionViewLayout:
extension UICollectionView: Configurable {
typealias Data = UICollectionViewLayout
func configure(data: Data) {
collectionViewLayout = data
}
}
Personally, I think this is a reasonable approach for classes where the init(frame:) initializer isn't appropriate.

Swift protocol and extension, I need to call overridden method or default extension method as per requirement

I have a protocol Vehicle and its extension like below:
protocol Vehicle {
func Drive()
}
extension Vehicle {
func Stop() {
print("iiiich...")
}
}
And I also have declaration like below for Stop Method
struct Car: Vehicle {
func Drive() {
print("Can Drive")
}
func Stop() {
print("yo stop")
}
}
let myCar = Car()
myCar.Drive()
myCar.Stop()
But its override the Stop Method
// Output
// Can Drive
// yo stop
And as per my requirement I need default method sometime and some time overridden method definition
Hey I got the answers that is to conform the protocol by the object call your default method rather than overriddedn, so we can call both defination as required
let honda: Vehicle = Car()
honda.Drive()
honda.Stop()
// Output
// Can Drive
// iiiich..
When we create a variable without type then this is static dispatch when a object conform a protocol only.
If you need the method declared in the protocol extension, just make the compiler think that the car is of type Vehicle:
let myCar = Car()
(myCar as Vehicle).Stop()
As already mentioned in the answers, the generic solution is to make sure that the instance that calls Stop() method is of type Vehicle (not Car). Nevertheless I would mention what's the logic behind it.
Personally, I think that there is a possibility to face this issue when it comes to work with the POP paradigm. Protocol extensions is a handy way the apply Polymorphism in our code, however it does leads to this "weird" behavior!
Static Dispatch:
Firstly, keep in mind that it is not a bug. In case of:
let honda: Vehicle = Car()
honda.Drive()
honda.Stop()
there is a manual cast to the honda as Vehicle, at this point the compiler will use static dispatch, which means that it would be recognizable which method should be called (Vehicle().Stop or Car().Stop) during the compile time. It selects the default implementation for Vehicle which is implemented in the extension, without the need of checking what is the concrete type.
Dynamic Dispatch:
In case of:
let myCar = Car()
myCar.Drive()
myCar.Stop()
nothing special goes here, it works exactly as expected. That's exactly the the meaning of dynamic dispatch, which leads to apply polymorphic operations during the run time.
To make it more clear, consider that you have another type that conforms to Vehicle protocol:
struct Bus: Vehicle {
func Drive() {
print("Bus Drive")
}
func Stop() {
print("Bus stop")
}
}
let myBus = Bus()
myCar.Drive()
myCar.Stop()
Obviously, the print("Bus stop") is the one which will be called, and actually that's the expected! The compiler is "smart" enough to recognize which method to be selected based on what is the concrete type (Bus().Stop).
Furthermore:
For better understanding of what's going on here, reviewing Understanding Swift Performance Apple session might be helpful.
You need a protocol with a default implementation that allows a struct param, that can do custom behaviors:
import UIKit
struct Car{
//Any properties
func drive(){
print("Yo Drive")
}
func stop(){
print("Yo Stop")
}
}
protocol Vehicle {
func drive(vehicle : Car?)
func stop(vehicle : Car?)
}
extension Vehicle where Self: UIViewController {
func drive(vehicle : Car? = nil) {
if (vehicle != nil){
vehicle?.drive()
}else{
print("drive default behavior")
}
}
func stop(vehicle : Car? = nil) {
if (vehicle != nil){
vehicle?.stop()
}else{
print("stop default behavior")
}
}
}
class ViewController : UIViewController, Vehicle {
func driving() {
drive() //will print drive default behavior
stop() //will print stop default behavior
let car = Car()
drive(vehicle: car) //will print yo drive!
stop(vehicle: car) //will print yo Stop!
}
override func viewDidLoad() {
driving()
}
}

How to call a protocol's default function declared in a protocol extension?

I'm trying to call a protocol's default function that was declared in an extension:
protocol Tester {
func printTest()
}
extension Tester {
func printTest() {
print("XXXXTestXXXX")
}
}
class TestController: UIViewController, Tester {
let testing = Tester()// error here
override func viewDidLoad() {
super.viewDidLoad()
testing.printTest()
}
}
The error ''Tester' cannot be constructed because it has no accessible initializers' keeps appearing when I try to create an instance of the protocol. Whats the best way to use default functions in protocols?
You have to call the implementer, in your case it's TestController so :
self.printTest() will work
protocol Proto {
// func testPrint() <- comment this out or remove it
}
extension Proto {
func testPrint() {
print("This extension called")
}
}
struct Bar: Proto {
func testPrint() {
print("Call from Structure")
(self as Foo).testPrint()
}
}
Bar().testPrint()
// Output: 'Call from Structure',
// 'This extension call'
Protocols cannot be instantiated - ever - lets think of them Contracts
Classes CAN supposed to confrom to 1..n protocols.
The fact that your protocol provides a default implementation, just enables a class that conforms to it to inherit this default functionality if it doesnt need to provide a custom implementation.
a very cheap way in your case would be:
let testing = self
since your TestController class already conforms to Tester.
(Note I create a retain cycle)
===
another way would be to not use testing but self.printTest()
(This might be what you want)
Id reread the apple swift book and check out protocols (what they are and how they work)

Optional functions for override in Swift classes?

I'm trying to make a simple game using Swift. The game has different levels, and each level requires the game to behave in slightly different ways. I decided to use a different class for each level, all inherited from a base level class.
This is the base:
import SpriteKit
class LevelBase {
var scene: GameScene! // Seems very dodgy
var blocks = [SKSpriteNode]()
init(scene: GameScene) { // Should this be required init?
self.scene = scene
}
func filterBlock(_ block: SKSpriteNode) {
blocks = blocks.filter() { $0 !== block } // Looks really dodgy to me
}
func update(time: TimeInterval) {
// For override
}
func levelUp() {
// For override
}
func postGenerate() {
// For override
}
}
However, to me, this class seems to be very badly written. I can't find any examples anywhere of functions created in a class just to be overwritten, which makes me think I'm doing something wrong. Should I be using extensions or protocols for optional functions like that? I don't quite understand how they work, so I haven't used any so far.
The second issue is that this class needs to be initialized with the game scene variable, since some levels need it to add or remove sprites. This seems especially dodgy, considering the class is created in the game scene's file.
Surely there's a better way?
I have no experience with SpriteKit, but from a general perspective you should consider to "Favour composition over Inheritance".
You would have one Level class that is not intended for subclassing but can be instantiated with objects or values that have different implementation.
Additionally you should use protocols to define those and you can add default implementation as protocol extensions.
final class Level {
init(levelImplementation: LevelImplementationType) {
self.levelImplementation = levelImplementation
}
let levelImplementation: LevelImplementationType
func navigate() {
levelImplementation.navigate()
}
func update(timeInterval: TimeInterval) {
levelImplementation.update(timeInterval: timeInterval)
}
}
The Level would be instantiated with an object or struct conforming to LevelImplementationType
protocol LevelImplementationType {
func navigate()
func update(timeInterval: TimeInterval)
}
Default implementations can be done via an extension.
extension LevelImplementationType {
func navigate() {
}
func update(timeInterval: TimeInterval) {
}
}
The LevelImpelmenation needs to conform to LevelImplementationType, but don't have any further constraints. i.e. they can have very different initialisers.
struct LevelImplementation1: LevelImplementationType {
// useses default implementation of `navigate` and `update` from extension
}
struct LevelImplementation2: LevelImplementationType {
// useses default implementation of `update` from extension
func navigate() {
}
}
struct LevelFileImplementation: LevelImplementationType {
init(with path: String) {
// read variables from file at path
}
func navigate() {
// navigate as given in file
}
}
Level instances cane be created like
let level1 = Level(levelImplementation: LevelImplementation1())
let level2 = Level(levelImplementation: LevelImplementation2())
let level3 = Level(levelImplementation: LevelFileImplementation(with: "path/to/file"))

Is use of generics valid in XCTestCase subclasses?

I have a XCTestCase subclass that looks something like this. I have removed setup() and tearDown methods for brevity:
class ViewControllerTests <T : UIViewController>: XCTestCase {
var viewController : T!
final func loadControllerWithNibName(string:String) {
viewController = T(nibName: string, bundle: NSBundle(forClass: ViewControllerTests.self))
if #available(iOS 9.0, *) {
viewController.loadViewIfNeeded()
} else {
viewController.view.alpha = 1
}
}
}
And its subclass that looks something like this :
class WelcomeViewControllerTests : ViewControllerTests<WelcomeViewController> {
override func setUp() {
super.setUp()
self.loadControllerWithNibName("welcomeViewController")
// Put setup code here. This method is called before the invocation of each test method in the class.
}
func testName() {
let value = self.viewController.firstNameTextField.text
if value == "" {
XCTFail()
}
}
}
In theory, this should work as expected -- the compiler doesn't complain about anything. But it's just that when I run the test cases, the setup() method doesn't even get called. But, it says the tests have passed when clearly testName() method should fail.
Is the use of generics a problem? I can easily think of many non-generic approaches, but I would very much want to write my test cases like this. Is the XCTest's interoperability between Objective-C and Swift the issue here?
XCTestCase uses the Objective-C runtime to load test classes and find test methods, etc.
Swift generic classes are not compatible with Objective-C. See https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID53:
When you create a Swift class that descends from an Objective-C class, the class and its members—properties, methods, subscripts, and initializers—that are compatible with Objective-C are automatically available from Objective-C. This excludes Swift-only features, such as those listed here:
Generics
...
Ergo your generic XCTestCase subclass can not be used by XCTest.
Well, actually its totally doable. You just have to create a class that will be sort of test runner. For example:
class TestRunner: XCTestCase {
override class func initialize() {
super.initialize()
let case = XCTestSuite(forTestCaseClass: ViewControllerTests<WelcomeViewController>.self)
case.runTest()
}
}
This way you can run all your generic tests.
I got it working in a kind of hacky way with protocols & associated types:
import XCTest
// If your xCode doesn't compile on this line, download the lastest toolchain as of 30 november 2018
// or change to where Self: XCTestCase, and do it also in the protocol extension.
protocol MyGenericTestProtocol: XCTestCase {
associatedtype SomeType
func testCallRunAllTests()
}
extension MyGenericTestProtocol {
// You are free to use the associated type here in any way you want.
// You can constraint the associated type to be of a specific kind
// and than you can run your generic tests in this protocol extension!
func startAllTests() {
for _ in 0...100 {
testPrintType()
}
}
func testPrintType() {
print(SomeType.self)
}
}
class SomeGenericTestInt: XCTestCase, MyGenericTestProtocol {
typealias SomeType = Int
func testCallRunAllTests() {
startAllTests()
}
}
class SomeGenericTestString: XCTestCase, MyGenericTestProtocol {
typealias SomeType = String
func testCallRunAllTests() {
startAllTests()
}
}
This approach (https://stackoverflow.com/a/39101121/311889) no longer works. The way I did it in Swift 5.2 is:
class MyFileTestCase: XCTestCase {
override func run() {
let suite = XCTestSuite(forTestCaseClass: FileTestCase<MyFile>.self)
suite.run()
super.run()
}
// At least one func is needed for `run` to be called
func testDummy() { }
}

Resources