Protocol downcasting in Swift - swift

I have the following situation where I have a generic protocol A that can have variants that inherit from it (e.g. B) and a class that implements a variant (C).
protocol A {}
protocol B: A {
var foo: String { get }
}
class C: B {
let foo: String = "foo"
}
Now, if I have an object of type A, but is actually a C, how can I get to stuff declared in B?
func foo() {
let c: A = C()
}
If I try to cast as in let b = c as B I get Cannot downcast from 'A' to non-#objc protocol type 'B'.

Your code looks good - the only problem is that the as operator requires objc compatibility. You can fix it by prefixing your protocols with the #objc attribute:
#objc protocol A {}
#objc protocol B: A {
var foo: String { get }
}
Unfortunately there's a downside on doing that: you lose the ability to use swift-specific features that are not available in objective C, such as enums, tuples, generics, etc.
More info here: Checking for Protocol Conformance

Related

Specialize generic function requirement on protocol inheritance

I have some protocol hierarchies on my code where I have protocols defining the objects I use and protocols defining functions to use with this objects.
The object protocols are inherited by other object protocols that add more functionality to the original protocols and so are the functions that use them. The problem is that I can't find a way to specialize the function to take only the inherited parameter.
Here's some code to clarify what I'm trying to do:
protocol A {
var foo: String { get set }
}
protocol B: A {
var bar: String { get set }
}
struct Test: B {
var foo: String = "foo"
var bar: String = "bar"
}
protocol UseAProtocol {
static func use<T: A>(_ obj: T)
}
protocol UseBProtocol: UseAProtocol {
}
extension UseBProtocol {
//If I change the requirement to <T: B> this won't conform to `UseAProtocol`.
static func use<T: A>(_ obj: T) {
print(obj.foo)
// print(obj.bar) - Since obj does not conform to `B` I can't access ".bar" here without a forced casting.
}
}
struct Manager: UseBProtocol {
}
Manager.use(Test())
What I want to do is make the use function on the UseBProtocol only accept objects that conform to B. B inherits from A, but when I change from <T:A> to <T:B> I got an error saying that Manager does not conform to UseAProtocol and I have to change it back to <T:A>.
I know I can do this using associatedtype and where clauses on the inherit protocols - that's what I use today - but I wanted to move the generic requirement to the method so I could group all of them together under the same struct (I have a lot of this hierarchies and by using associatedtype I must use one struct by hierarchy). When the Conditional Conformances came to Swift this would be possible with associatedtype, but until them...
I could also use as! to force the casting from A to B on the UseBProtocol implementation, but that's a really bad solution and the error would be throw only at runtime.
Is there any way to achieve what I'm looking for?
It seems like what you are actually looking for is an associatedType in UseAProtocol rather than making the use function generic.
By declaring an associated type in UseAProtocol and changing the function signature of use to static func use(_ obj: ProtocolType) your code compiles fine and you can access both foo and bar from Manager.
protocol AProtocol {
var foo: String { get set }
}
protocol BProtocol: AProtocol {
var bar: String { get set }
}
struct Test: BProtocol {
var foo: String = "foo"
var bar: String = "bar"
}
protocol UseAProtocol {
associatedtype ProtocolType
static func use(_ obj: ProtocolType)
}
protocol UseBProtocol: UseAProtocol {
}
extension UseBProtocol {
static func use(_ obj: BProtocol) {
print(obj.foo)
print(obj.bar)
}
}
struct Manager: UseBProtocol {
}
Manager.use(Test()) //prints both "foo" and "bar"

Re-conforming to a protocol / conforming multiple times / overriding associated type

Given the following protocol with an associated type:
protocol P {
associatedtype T
func f(t: T)
}
And the class A that conforms to protocol P:
class A: P {
func f(t: String) {
print(t)
}
}
Is there any way to override the conformance to 'P' in a subclass 'B' so that we can change the associated type 'T' ?
class B: A, P { // Redundant conformance of 'B' to protocol 'P'
func f(t: Int) {
super.f("Hello")
print(2 * t)
}
}
Writing that B conforms to P is redundant and gives an error.
B would be conforming to the protocol twice, which isn't possible even without subclassing.
I can think of scenarios where such a feature might be useful. Are there any reasons why this shouldn't be possible to do?
Edit:
What I'm asking is if there is any way to have A be P<T> and B be P<U> even if B is a subclass of A, i.e. override the associated type.
Your subclass already inherits the adoption of the protocol from its superclass, so there is no need to override the conformance of protocol in a subclass.
Reason:
protocol P {
var name:String{get set}
}
class A:P {
var name:String = "Swift"
}
class B:A {
override var name:String{
get{
return "Hello"
}set{
}
}
}
After conforming class B to protocol P you should implement the properties and methods.In normal cases, compiler will warn you to add the
required methods and properties..
But If that happens in this case,there will be conflict in name with the overridden ones..So this should not happen.And so you will get compiler error as
Re-conforming to a protocol

Swift Inherited Protocols and Conformance Checking for the Parent-Protocol

I noticed that in Swift 2.2, if I have a protocol A, and then a protocol B: A which inherits from A, checking for conformance to the parent protocol like this fails: if objectConformingToBWhichInheritsFromA is A evaluates to false, as does the as? variant.
Am I doing something wrong?
My Playground tells a different story
protocol A { }
protocol B: A { }
class Foo: B { }
let foo = Foo()
foo is A // true
foo is B // true

Swift protocol as generic type

I have this kind of code:
protocol MyProtocol {
}
class P1: MyProtocol {
}
class P2: MyProtocol {
}
class C <T: MyProtocol> {
}
Then i need to define a variable to delegate all kinds of C<MyProtocol>:
var obj: C <MyProtocol>
But compile error comes:
Using 'MyProtocol' as a concrete type conforming to protocol 'MyProtocol' is not supported
How can I do?
This code:
class C <T: MyProtocol> { }
Means that C is a generic class that can specialize on any type T that conforms to MyProtocol.
When you declare:
var obj: C <MyProtocol>
You are (I think) trying to say that the var obj will be an instance of C specialized to some type that conforms to MyProtocol,but you can't specialize a generic class on a protocol type, because there is no such thing as a direct concrete instance of a protocol. There can only be instances of a type conforming to the protocol. And there can theoretically be many different types that conform to the protocol. So that notation doesn't really tell the compiler which specific specialization of C to use.
This shouldn't be a problem though, because you can write things like:
var obj: C<P1> = C<P1>()
or
var obj = C<P2>() // type is inferred
And within your class C you can still treat any uses of T as conforming to MyProtocol. So I think this should give you everything you need, as long as you remember that an instance of a generic class must be specialized to a single specific concrete type, not a protocol which could represent many possible concrete types.
You usually don't need to declare type for a Swift's variable. The compiler can infer type in most cases. Also, for generic class, you should let the compiler figure out what the generic classes resolve to:
class C<T: MyProtocol> {
var value: T
init (value: T) {
self.value = value
}
}
var obj = C(value: P1()) // type: C<P1>
// or:
var obj = C(value: P2()) // type: C<P2>

Protocol extensions on Structs causes compile error 'Self' constrained to non-protocol type

I'm attempting to apply a constrained protocol extension to a struct (Swift 2.0) and receiving the following compiler error:
type 'Self' constrained to non-protocol type 'Foo'
struct Foo: MyProtocol {
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
protocol MyProtocol {
func bar()
}
extension MyProtocol where Self: Foo {
func bar() {
print(myVar)
}
}
let foo = Foo(myVar: "Hello, Protocol")
foo.bar()
I can fix this error by changing struct Foo to class Foo but I don't understand why this works. Why can't I do a where Self: constrained protocol a struct?
This is an expected behaviour considering struct are not meant to be inherited which : notation stands for.
The correct way to achieve what you described would be something like equality sign like:
extension MyProtocol where Self == Foo {
func bar() {
print(myVar)
}
}
But this doesn't compile for some stupid reason like:
Same-type requirement makes generic parameter Self non-generic
For what it's worth, you can achieve the same result with the following:
protocol FooProtocol {
var myVar: String { get }
}
struct Foo: FooProtocol, MyProtocol {
let myVar: String
}
protocol MyProtocol {}
extension MyProtocol where Self: FooProtocol {
func bar() {
print(myVar)
}
}
where FooProtocol is fake protocol which only Foo should extend.
Many third-party libraries that try to extend standard library's struct types (eg. Optional) makes use of workaround like the above.
I just ran into this problem too. Although I too would like a better understanding of why this is so, the Swift language reference (the guide says nothing about this) has the following from the Generic Parameters section:
Where Clauses
You can specify additional requirements on type parameters and their
associated types by including a where clause after the generic
parameter list. A where clause consists of the where keyword, followed
by a comma-separated list of one or more requirements.
The requirements in a where clause specify that a type parameter
inherits from a class or conforms to a protocol or protocol
composition. Although the where clause provides syntactic sugar for
expressing simple constraints on type parameters (for instance, T:
Comparable is equivalent to T where T: Comparable and so on), you can
use it to provide more complex constraints on type parameters and
their associated types. For instance, you can express the constraints
that a generic type T inherits from a class C and conforms to a
protocol P as <T where T: C, T: P>.
So 'Self' cannot be a struct or emum it seems, which is a shame. Presumably there is a language design reason for this. The compiler error message could certainly be clearer though.
As Foo is an existing type, you could simply extend it this way:
struct Foo { // <== remove MyProtocol
let myVar: String
init(myVar: String) {
self.myVar = myVar
}
}
// extending the type
extension Foo: MyProtocol {
func bar() {
print(myVar)
}
}
From The Swift Programming Language (Swift 2.2):
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined.