Swift: Passing conformed object instances to generic methods - swift

I have the following playground:
// : Playground - noun: a place where people can play
import Foundation
// Define a protocol
protocol MyProtocol
{
func getMeAString() -> String!
}
// Create a class that conforms to the protocol
class ClassThatConformsToProtocol: MyProtocol
{
func getMeAString() -> String! {
return "hey!"
}
}
// Define a function that takes a generic, but ensure the generic conforms to the protocol
func logTheThing<T: MyProtocol>(theThing: T!)
{
theThing.getMeAString()
}
// Let's create an instance
let instanceOfClassThatConforms = ClassThatConformsToProtocol()
// We're now going to see if we can pass the object to our generic method
logTheThing(instanceOfClassThatConforms)
// It works!
// Let's create another method, but this one only knows the protocol its parameter conforms to, the in implementing class
func takeInAnObjectThatWeOnlyKnowItsProtocol(object:MyProtocol)
{
logTheThing(object) // error: cannot convert value of type 'MyProtocol' to expected argument type '_!'
}
As mentioned, I receive an error stating:
error: cannot convert value of type 'MyProtocol' to expected argument
type '_!'
For what reason would I not be able to pass this conformed object into the generic method?
If the compiler knows an object conforms to the protocol, for what reason would I NOT be able to pass it into a generic method?

A protocol does not conform to itself. You must make this generic:
func takeInAnObjectThatWeOnlyKnowItsProtocol<T: MyProtocol>(object: T)
{
logTheThing(object)
}
To the next question: why doesn't a protocol conform to itself? Because it doesn't. Eventually it probably will, but it doesn't today.
That said, given this specific code, there's no reason to make this generic at all. Just pass the protocol and it'll do exactly what you want:
func logTheThing(theThing: MyProtocol) {
theThing.getMeAString()
}
func takeInAnObjectThatWeOnlyKnowItsProtocol(object: MyProtocol) {
logTheThing(object)
}
Passing object: MyProtocol means "any type that conforms to MyProtocol" and that matches in both places. Passing <T: MyProtocol>(object: T) means "a specific, concrete type that conforms to MyProtocol" and "any type that conforms to MyProtocol" is not a "specific, concrete type" and so fails (today; again, they'll probably fix that some day).
(Unrelated note: There is never a good reason to return String! in Swift. Just return String. And you'd get a better error message and fewer other little problems if you don't use T! and just use T in your logging call. Unless you're bridging to ObjC, there is almost never a reason to pass or return ! types. They only make sense in pure Swift for properties.)

Related

Swift Protocol Optional conformance via Non-Optional

I have a protocol with an optional property.
Most of the types that conform to this protocol will have a matching optional property. However, one has a non-optional property of the same type and name.
protocol SomeProtocol {
var foo: Int? { get }
}
struct StructA: SomeProtocol {
let foo: Int?
}
struct StructB: SomeProtocol {
let foo: Int // Type 'StructB' does not conform to protocol 'SomeProtocol'
}
Pressing Xcode's "Fix - Do you want to add protocol stubs?" button adds the optional version of the property, but the structure now has invalid duplicated variable names:
struct StructB: SomeProtocol {
let foo: Int
var foo: Int? { return foo } // Invalid redeclaration of 'foo'
}
In the { get }-only case, I had assumed that this would "just work" due to the non-optional always satisfying the constraints of the optional, similar to how you can return a non-optional in a function with an optional return type. But apparently that is not the case.
This works the same for functions as well; a protocol's func bar() -> Int? is not satisfied by a conforming type declaring func bar() -> Int.
Is there any way around this issue? I would prefer not to rename the variables or add intermediate getters.
Has this situation been considered for Swift? What is the rational for not allowing a non-optional to satisfy an optional protocol variable?
If the protocol provides a default implementation that returns an optional:
protocol SomeProtocol {
var foo: Int? { get }
}
extension SomeProtocol {
var foo: Int? { return nil }
}
protocol-conforming types can then provide an overriding non-optional version of the variable/function:
struct StructB: SomeProtocol {
let foo: Int
}
I found this discussed on the Swift Evolution forum:
At the first glance I thought there is a rule that allows us to satisfy protocol requirements with non-optional types, but this resulted in an error. Only after further investigation I noticed that a default implementation must exist in order to 'kind of override' the requirement with a non-optional version.
https://forums.swift.org/t/how-does-this-rule-work-in-regard-of-ambiguity/19448
This Swift team also discusses allowing non-optional types to satisfy optional-value protocols:
Would it make any sense to allow protocol requirement satisfaction with non-optional types, like with failable init's? (Probably with some implicit optional promotion.)
Yep, totally! Except for the part where this changes the behavior of existing code, so we'd have to be very careful about it. This is considered part of [SR-522] Protocol funcs cannot have covariant returns
which is tracked on Stack Overflow here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
The extension-with-default-implementation solution offered by pkamb is helpful in terms of getting the compiler to recognize conformance, but you need to be aware of the strange behavior this can produce.
An object of the newly conforming type will return a different value depending on what type you cast it to (this also includes passing as to a parameter whose type is the protocol):
let bar = StructB(foo: 7)
let baz: SomeProtocol = bar
bar.foo // evaluates to 7
baz.foo // evaluates to nil (surprise!)
As someone recently commented on a related Swift bug ticket: "This can be quite surprising and perhaps could be considered a bug of its own?"
It definitely tripped me up.

Swift protocols behavior

Let's assume I have protocol FooProtocol in Swift with one method with its default implementation:
protocol FooProtocol {
func foo()
}
extension FooProtocol {
func foo() {
print("protocol")
}
}
and class FooClass with cast instance:
class FooClass : FooProtocol {
func foo() {
print("class")
}
}
let A = FooClass() as FooProtocol
A.foo()
As expected, "class" will be printed in console.
However, if I make foo() method of my protocol being optional - "protocol" will be printed instead.
#objc protocol FooProtocol {
#objc optional func foo()
}
But if I call A.foo?() rather than A.foo() - "class" will be printed again.
I wonder, just for theoretical purposes, what the hell is happening here? Any explanations are appreciated.
You cannot call optional protocol method without optional chaining. I.e. if you comment out
extension FooProtocol {
func foo() {
...
and you try to call A.foo(), you will get a compilation error:
value of optional type '(() -> ())?' must be unwrapped to a value of type '() -> ()'
As you see the signature of optional is not even the same as non-optional. So by calling A.foo(), you are not calling method you implemented, you are calling default protocol implementation directly.
It also makes sense since primary reason for #objc optional is interpolation with Objective C, where default protocol implementation won't be visible. So relying on protocol function for objc function would produce different result on swift and objective c, which is undesired.
I cannot say if there's any loophole to use both optional and default protocol implementation on the same function. But question is: do you really need it?
If you are concerned with interpolation, you need equal solution for both Swift and Objective c, and that means you rather need basic protocol implementation, which both Swift and objc can inherit
If you need no interpolation, you don't really need optional, and can rely on default protocol implementation alone.

Method ... in non-final class ... cannot be implemented in a protocol extension because it returns 'Self' and has associated type requirements

This is my code:
protocol Person {
associatedtype Homework
static func generate(homeWork: Homework) -> Self
}
extension Person {
static func generate(homeWork: Homework) -> Self {
fatalError()
}
}
// Method 'generate(homeWork:)' in non-final class 'Teacher'
// cannot be implemented in a protocol extension because it
// returns 'Self' and has associated type requirements
class Teacher: Person {
typealias Homework = Int
}
Despite the strange naming of types, I am curious why it isn't possible to conform a non-final class to a protocol which has a method that returns Self and having a associatedtype as parameter type.
Note: I am not asking how to fix this problem (because I can just mark my class final). I want to know why this isn't possible.
I think it is strange. Protocols with associatedtypes and Self is always a struggle ofcourse, but when using one of two (returning Self/associatedtype protocol type in parameter) compiles the code just fine.
Why can't I conform to a protocol which has a default implementation of a method whereas the method has a return type of Self and associatedtype in a parameter, but when only using one of them the code compiles? Why is both a problem when dealing with inherence (because marking the class as final fixes the error message, so it has to be something with inherence)?
Looks like its because of a compiler limitation. Here's some relevant discussion from people on the Swift team:
https://twitter.com/_danielhall/status/737782965116141568
The original tweet shows what would happen if we tried to create a subclass from Teacher, the compiler would not be able to identify it as conforming to the Person protocol.
Specifically relevant from the swift team in that thread: "we don't support subtyping when matching protocol requirements. I consider it a missing feature in the language"
So knowing the compiler can't recognize subclasses as conforming to the protocol in these situations they decide to force a final class.

Storing a generic conforming to an associated type inside A collection

I am trying to store a generic who uses an an associated type, however when trying to create a type which should conform to the generic type I describe in the generic list at the top of class A, but I get the error.
"Cannot invoke 'append' with an argument list of type '(B)'"
How can I properly declare the generic so that this code works?
class A<DataType: Any, AssociatedType: Runable> where
AssociatedType.DataType == DataType {
var array = Array<AssociatedType>()
func addAssociatedValue(data: DataType) {
array.append(B(data: data))
}
func runOnAll(with data: DataType) {
for item in array {
item.run(with: data)
}
}
}
class B<DataType>: Runable {
init(data: DataType) { }
func run(with: DataType) { }
}
protocol Runable {
associatedtype DataType
func run(with: DataType)
}
I am also using Swift 4.2 so if there is a solution that uses one of the newer Swift features that will also work as a solution.
B conforms to Runnable, yes, but you can't put it into an array that's supposed to store AssociatedTypes. Because the actual type of AssociatedType is decided by the caller of the class, not the class itself. The class can't say, "I want AssociatedType to always be B". If that's the case, you might as well remove the AssociatedType generic parameter and replace it with B. The caller can make AssociatedType be Foo or Bar or anything conforming to Runnable. And now you are forcing to put a B in.
I think you should rethink your model a bit. Ask yourself whether you really want AssociatedType as a generic parameter.
You could consider adding another requirement for Runnable:
init(data: DataType)
And add required to B's initializer. This way, you could write addAssociatedValue like this:
func addAssociatedValue(data: DataType) {
array.append(AssociatedType(data: data))
}

Swift: cannot convert protocol constrained to type to value of that type

I am trying to constrain the input to a method to a list of instances conforming to a specific protocol. In my particular case I do not have control over the method and cannot change its arguments. The method takes an array of a specific type as input. I was thinking I could make a protocol constrained to that type and then pass in an array of instance conforming to that protocol. But the compiler won't let me.
Why can't I do the following ?
protocol SomeProtocol where Self: SomeClass {
}
class SomeClass: SomeProtocol {
}
func doSomething(input: [SomeClass]) {
}
let someClasses: [SomeProtocol] = [SomeClass(), SomeClass()]
// Cannot convert value of type '[SomeProtocol]' to expected argument type '[SomeClass]'
doSomething(input: someClasses)
doSomething's input is an [SomeClass] but you are passing a [SomeProtocol] to it. SomeProtocol is not a SomeClass but SomeClass is a SomeProtocol. To satisfy the compiler, you just have to change the type of someClasses to [SomeClass]:
let someClasses = [SomeClass(), SomeClass()]
Or you could change the requirement of the parameter:
func doSomething(input: [SomeProtocol])