I'm writing a factory class that is trying to work with custom protocol defined functions. The compiler throws an error, because I don't know how to add a protocol definition to a function parameter.
Example:
protocol MyCustomFunctions {
func customFunction()
}
class MyVC: UIViewController, MyCustomFunctions {
func customFunction() {}
}
class Factory {
func createButton(specificVC: UIViewController) // need protocol here
{
specificVC.customFunction() // error thrown
}
}
How can one specific a protocol during a variable definition?
Or is there another way?
First of all ,convention says classes start with a Capital letter.
class MyVC: UIViewController, MyCustomFunctions {
func customFunction() {}
}
Then what you need is the correct type in the argument
class factory: NSObject {
func createButton(specificVC: MyVC) // you need a class that conforms to protocol here.
{
specificVC.customFunction() // no error anymore
}
}
You have another option. You can simply promise in the argument that you won't disclose the full type of the object ,you will only say it's an opaque object that conforms to protocol.
class factory: NSObject {
func createButton(specificVC: MyCustomFunctions) // you need a class that conforms to protocol here.
{
specificVC.customFunction() // no error anymore
}
}
BONUS:
The way you could have reasoned about this and find an answer is this>
Error is thrown when I call specificVC.customFunction()...Hmmm...so this object can only run this function if it is of type that actually HAS the function. So let's take a look at the argument type - UIViewController - ..UIViewController certainly doesn't have this function. It's the MyVC or the Protocol.
Type safety in Swift is very strict. Just "follow the type flow" and you will be good.
Related
I've encountered a problem that is explained in the code below (Swift 3.1):
protocol MyProtocol {
func methodA()
func methodB()
}
extension MyProtocol {
func methodA() {
print("Default methodA")
}
func methodB() {
methodA()
}
}
// Test 1
class BaseClass: MyProtocol {
}
class SubClass: BaseClass {
func methodA() {
print("SubClass methodA")
}
}
let object1 = SubClass()
object1.methodB()
//
// Test 2
class JustClass: MyProtocol {
func methodA() {
print("JustClass methodA")
}
}
let object2 = JustClass()
object2.methodB()
//
// Output
// Default methodA
// JustClass methodA
So I would expect that "SubClass methodA" text should be printed after object1.methodB() call. But for some reason default implementation of methodA() from protocol extension is called. However object2.methodB()call works as expected.
Is it another Swift bug in protocol method dispatching or am I missing something and the code works correctly?
This is just how protocols currently dispatch methods.
A protocol witness table (see this WWDC talk for more info) is used in order to dynamically dispatch to implementations of protocol requirements upon being called on a protocol-typed instance. All it is, is really just a listing of the function implementations to call for each requirement of the protocol for a given conforming type.
Each type that states its conformance to a protocol gets its own protocol witness table. You'll note that I said "states its conformance", and not just "conforms to". BaseClass gets its own protocol witness table for conformance to MyProtocol. However SubClass does not get its own table for conformance to MyProtocol – instead, it simply relies on BaseClass's. If you moved the : MyProtocol down to the definition of SubClass, it would get to have its own PWT.
So all we have to think about here is what the PWT for BaseClass looks like. Well, it doesn't provide an implementation for either of the protocol requirements methodA() or methodB() – so it relies on the implementations in the protocol extension. What this means is that the PWT for BaseClass conforming to MyProtocol just contains mappings to the extension methods.
So, when the extension methodB() method is called, and makes the call out to methodA(), it dynamically dispatches that call through the PWT (as it's being called on a protocol-typed instance; namely self). So when this happens with a SubClass instance, we're going through BaseClass's PWT. So we end up calling the extension implementation of methodA(), regardless of the fact that SubClass provides an implementation of it.
Now let's consider the PWT of JustClass. It provides an implementation of methodA(), therefore its PWT for conformance to MyProtocol has that implementation as the mapping for methodA(), as well as the extension implementation for methodB(). So when methodA() is dynamically dispatched via its PWT, we end up in its implementation.
As I say in this Q&A, this behaviour of subclasses not getting their own PWTs for protocols that their superclass(es) conform to is indeed somewhat surprising, and has been filed as a bug. The reasoning behind it, as Swift team member Jordan Rose says in the comments of the bug report, is
[...] The subclass does not get to provide new members to satisfy the conformance. This is important because a protocol can be added to a base class in one module and a subclass created in another module.
Therefore if this was the behaviour, already-compiled subclasses would lack any PWTs from superclass conformances that were added after the fact in another module, which would be problematic.
As others have already said, one solution in this case is to have BaseClass provide its own implementation of methodA(). This method will now be in BaseClass's PWT, rather than the extension method.
Although of course, because we're dealing with classes here, it won't just be BaseClass's implementation of the method that's listed – instead it will be a thunk that then dynamically dispatches through the class' vtable (the mechanism by which classes achieve polymorphism). Therefore for a SubClass instance, we'll wind up calling its override of methodA().
A very short answer that a friend shared with me was:
Only the class that declares the conformance gets a protocol witness table
Meaning a subclass having that function has no effect on how the protocol witness table is setup.
The protocol witness is a contract only between the protocol, it's extensions, and the concrete class that implements it.
Well I suppose the subclass method A is not polymorphic because you can't put the override keyword on it, since the class doesn't know the method is implemented in an extension of the protocol and thus doesn't let you override it. The extension method is probably stepping on your implementation in runtime, much like 2 exact category methods trump each other with undefined behavior in objective C. You can fix this behavior by adding another layer in your model and implementing the methods in a class rather than the protocol extension, thus getting polymorphic behavior out of them. The downside is that you cannot leave methods unimplemented in this layer, as there is no native support for abstract classes (which is really what you're trying to do with protocol extensions)
protocol MyProtocol {
func methodA()
func methodB()
}
class MyProtocolClass: MyProtocol {
func methodA() {
print("Default methodA")
}
func methodB() {
methodA()
}
}
// Test 1
class BaseClass: MyProtocolClass {
}
class SubClass: BaseClass {
override func methodA() {
print("SubClass methodA")
}
}
let object1 = SubClass()
object1.methodB()
//
// Test 2
class JustClass: MyProtocolClass {
override func methodA() {
print("JustClass methodA")
}
}
let object2 = JustClass()
object2.methodB()
//
// Output
// SubClass methodA
// JustClass methodA
Also relevante answer here: Swift Protocol Extensions overriding
In your code,
let object1 = SubClass()
object1.methodB()
You invoked methodB from an instance of SubClass, but SubClass does not have any method named methodB. However its super class, BaseClass conform to MyProtocol, which has a methodB methodB.
So, it will invoke the methodB from MyProtocal. Therefore it will execute the methodA in extesion MyProtocol.
To reach what you expect, you need implement methodA in BaseClass and override it in SubClass, like the following code
class BaseClass: MyProtocol {
func methodA() {
print("BaseClass methodA")
}
}
class SubClass: BaseClass {
override func methodA() {
print("SubClass methodA")
}
}
Now, output would become
//Output
//SubClass methodA
//JustClass methodA
Although the method can reach what you expect, but I'm not sure this kind of code struct is recommended.
Couldn't get my head around how protocol generic types work in Swift. Same solution in Java or Kotlin would work without problems.
protocol ResultProtocol {
associatedtype T: ResultTypeProtocol
var result: T? { get set }
}
protocol ResultTypeProtocol {
func didGet()
}
protocol InteractorProtocol: ResultProtocol where T == InteractorResultProtocol {
func someLogicRelatedToInteractor()
}
protocol InteractorResultProtocol: ResultTypeProtocol {
func interactorResult()
}
class Interactor: InteractorProtocol {
typealias T = InteractorResultProtocol
var result: InteractorResultProtocol?
func someLogicRelatedToInteractor() {}
}
I get 2 errors in my code.
First one is when I put where generic constraint on another protocol
Second error is that my Interactor class doesn't conform to the protocol. When I click fix it will add another 'typealias T = type' and want me specify T again.
I want to know if there is another way to achieve this in Swift or how to fix this problem.
Idea behind this is to extend my interactor classes with generic property result which is used as delegate for other layers. Interactor is used via his protocol and it's injected in all other classes.
As #vadian comment says: "The type of the typealias must be a concrete type, not a protocol".
But if you want this class to be used with different InteractorResultProtocol then you can use generic for Interactor class also. And define T later.
class Interactor<ResultProtocolType: InteractorResultProtocol>: InteractorProtocol {
typealias T = ResultProtocolType
var result: ResultProtocolType?
func someLogicRelatedToInteractor() {}
}
Usage:
struct MyInteractorResultProtocol: InteractorResultProtocol {
func interactorResult() {}
func didGet() {}
}
let interactor = Interactor<MyInteractorResultProtocol>()
I have a project using the NVActivityIndicatorView library and I am trying to extract some logic from two view controllers. Both view controllers conform to NVActivityIndicatorViewable whose definition is:
// From the libaray. I don't want to modify this.
public protocol NVActivityIndicatorViewable {}
public extension NVActivityIndicatorViewable where Self: UIViewController
{
func startAnimating( ... ) { ... }
func stopAnimating() { ... }
}
And as a result, I expected to be able to pass one of these view controllers in and use the startAnimation and stopAnimation methods on it.
func sharedLogic(sender: NVActivityIndicatorViewable)
{
sender.startAnimating( ... )
sender.stopAnimating()
}
However, this fails with the compiler error 'NVActivityIndicatorViewable' requires that 'NVActivityIndicatorViewable' inherit from 'UIViewController'
Trying this with sender: UIViewController, this fails with the compile time error Value of 'UIViewController' has no member 'startAnimating' as I expected.
func sharedLogic(sender: UIViewController)
{
sender.startAnimating( ... )
sender.stopAnimating()
}
I have found two potential solutions:
Create an empty subclass that specifies both these types: (This new type doesn't contain any logic)
class ActivityIndicatorViewController: UIViewController, NVActivityIndicatorViewable { }
Use an extension to specify all view controllers can be activity indicators: (This causes redundant conformance errors on many classes)
extension UIViewController: NVActivityIndicatorViewable { }
Can I accomplish this without creating a new type?
Environment settings:
Xcode version: 10.1
iOS Deployment Target: 9.0
Swift version: 3
What you want here is a composition type - a type that both inherits from UIViewController and conforms to NVActivityIndicatorViewable:
UIViewController & NVActivityIndicatorViewable
You can use this directly as the parameter type for your method:
func sharedLogic(sender: UIViewController & NVActivityIndicatorViewable)
Or you can create a typealias for it (though I can't think of a shorter name):
typealias SomeShorterName = UIViewController & NVActivityIndicatorViewable
And then you can use SomeShorterName as the parameter type.
You set constraint on the protocol and extended the constrained one. So if you want those two functions, you need a UIViewContoller conformed to your protocol.
Move functions to the original protocol to get what you need.
public protocol NVActivityIndicatorViewable {
func startAnimating( ... )
func stopAnimating()
}
Update due to comment:
If you want to leave the original Protocol untouched, use composition type for the function:
func sharedLogic(sender: UIViewController & NVActivityIndicatorViewable) {
sender.startAnimating()
sender.stopAnimating()
}
TL;DR
How can I define optional methods in a swift protocol that accept custom typed parameters?
The case
I understand that the way to define optional methods in a swift protocol is using the #objc flag. Like so:
#objc protocol someSweetProtocol {
optional func doSomethingCool()
}
But when I wanted to use my custom type for the parameter, like so:
#objc protocol someSweetProtocol {
optional func doSomethingCool(🚶🏼: HSCoolPerson)
}
I got this error:
Which is not cool.
How can this problem be solved? Also can you explain why this is happening?
Addendum
This is how HSCoolPerson is defined:
class HSCoolPerson {
...
...
...
}
Nothing special there...
The problem is this:
class HSCoolPerson {
// ...
}
As the error message plainly tells you, that type, the class HSCoolPerson, is completely invisible to Objective-C. And a protocol optional method is an Objective-C language feature; Swift merely borrows it, as it were. (That's why you have to say #objc protocol to get this feature.) So any time you want to define a protocol optional method, you have to do it in a way that Objective-C can understand, because it is Objective-C that is going to do the work for you.
To expose this class to Objective-C, simply derive it from NSObject:
class HSCoolPerson : NSObject {
// ...
}
Problem solved.
Put the default implementations in an extension, like so:
class HSCoolPerson {}
protocol SomeSweetProtocol {
func doSomethingCool()
}
extension SomeSweetProtocol {
func doSomethingCool(🚶🏼: HSCoolPerson) {
// default implementation here
}
}
class SomeSweetClass: SomeSweetProtocol {
// no implementation of doSomethingCool(_:) here, and no errors
}
you can declare func like that in protocol
#objc optional func doSomethingCool(🚶🏼: HSCoolPerson)
This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd