Why Doesn't The Swift Compiler Detect Properties of Protocols - swift

I am using an NSOperation that conforms to SomeProtocol that has a results property
let op : NSOperation, SomeProtocol = ...
op.completionBlock = {
print(op.results)
}
I get the following error:
Value of type 'NSOperation' has no member 'results'
I know that I can subclass NSOperation to get the intended behaviour, but can I achieve what I want using protocols?

That code shouldn't even get that far... unlike Objective-C, Swift does not allow specifying a variable as a combination of both a concrete type AND a protocol. You can only declare a variable to be of a specific type, a specific protocol, or a composition of protocols, e.g.
let op : protocol<SomeProtocol, AnotherProtocol> = ...
But there is currently no way to declare a variable as being of the specific type NSOperation AND conforming to the protocol SomeProtocol

Related

Swift protocols mutability [duplicate]

This question already has answers here:
Read-only properties of protocols in Swift
(3 answers)
Why am I allowed to set a read only property of a protocol using a struct that inherits said protocol?
(3 answers)
Closed 2 years ago.
I'm currently a bit confused about gettable properties in protocols. Consider this example:
protocol Person {
var name: String { get }
}
I expected the name property to be read-only, but I found that you could change the value without compiler complaints:
struct Driver: Person {
var name: String
}
var driver = Driver(name: "Ryan")
driver.name = "Changed!"
If we define driver with let keyword, then compiler raises the error, but if I understand correctly, it has nothing to do with protocols, as constant structs are immutable by design in Swift.
Method interactions behave as I would've expected:
extension Person {
mutating func changeName(_ newName: String) {
self.name = newName // Error: 'name' is a get-only property
}
}
I'm new to Swift, and the nuance mentioned may not have any practical use, but this behavior made me ask myself if I lack some basic understanding of how structs work.
The protocol requirement is
a variable name which can be read
which doesn't mean that the variable in a struct adopting this protocol is necessarily read-only.
In the code you are changing the variable directly in the Driver type, the protocol is not involved.
On the other hand if you annotate the protocol type you get the expected error
var driver : Person = Driver(name: "Ryan")
driver.name = "Changed!" // Cannot assign to property: 'name' is a get-only property
A protocol only declares the required interface, but not the full interface of conforming types. Your conforming types can add extra properties/methods that aren't required by the protocol.
The same is true for getters and setter. If a protocol property requirement is get, that means the conformant type must have a getter for the property, but doesn't mean it cannot have a setter for it as well.
However, the same doesn't work the other way around. If a protocol declares a property as { get set }, that property must have a setter (be mutable).
The apple documentation explains this very well.
The getter and setter requirements can be satisfied by a conforming type in a variety of ways. If a property declaration includes both the get and set keywords, a conforming type can implement it with a stored variable property or a computed property that is both readable and writeable (that is, one that implements both a getter and a setter). However, that property declaration can’t be implemented as a constant property or a read-only computed property. If a property declaration includes only the get keyword, it can be implemented as any kind of property.

Swift: Protocol With associatedType

I have a question about Protocol with associated type, why I can not make the protocol a type of my instance for example:
I know I can use Type Erasure to fix the issue, but why protocol with an associated type does not like to be a type of an instance, and if you will say because the associated type is also used as a constraint, well I want to implement the properties inside the protocol not inside its extensions since protocol extensions has the power to control who can access its properties, why we still have this issue.
Thank you.
There are lots of articles and answers (like this one) out there describing why but in summary, It needs associatedtype. Variables can not have an associatedtype. So alongside with Type Erasure method (that you don't want), you can simply make it opaque with adding some keyword to the type:
var objectA: some ProtocolA = A()

How can I get conditional protocol conformance with a protocol's parent?

I have the following setup:
protocol Resource : Codable {}
class A<T> {}
extension A where T : Codable {
func doThingWithCodable() {}
}
let a = A<Resource>()
a.doThingWithCodable()
//Error: Protocol type 'Resource' cannot conform to Codable because
//only concrete types can conform to protocols.
I understand the error message, and I've read numerous Q&A's on generics, protocols and conditional conformance. I have a number of classes that conform to the Resource protocol, so it'd be really convenient if there was a way to let A<Resource> know that it will always be working with concrete types that conform to Codable, so I could still have one instance of A<Resource>, and that instance could have access to its conditional Codable methods. I did think about just making an instance like A<Codable>, but I need some of the properties that are inside the Resource protocol too.
Is there a swifty way to resolve this besides creating a new instance of A for each concrete type that conforms to Resource?
The question as posed, "How can I get conditional protocol conformance with a protocol's parent?" is meaningless, because a protocol always conforms with its parent; there is no "conditional" about it.
As for your actual code, the problem is the phrase A<Resource>. Saying A<Resource> is not a correct resolution of A's T. You need to resolve T as a class, struct, or enum — not as a protocol.
For example, if you have a class B that conforms to Resource, you can declare
let a = A<B>()
and all is well.
if there was a way to let A<Resource> know that it will always be working with concrete types that conform to Codable
Well, as I said, your code compiles fine as soon as A is working with a concrete type that does conform to Codable. So if that's what A will always be working with, there's nothing more to do. You could of course tell A that its T will always conform to Resource (which by definition will always conform to Codable):
class A<T:Resource> {}
Just think about this:
let a = A<Resource>()
This statement is trying to create an instance of class A and assigning it to a. The compiler doesn't know what this resource exactly is and how much memory it should allocate and what optimization it should run; because here Resource can be anything.
To resolve that, you need to provide little more information i.e. which you are already aware of.
struct CoffeeResouce: Resource {
let isWarm: Bool
}
Now when you write this:
let a: A<CoffeeResource> = A()
The compiler is well about the type and it's memory requirements. This is one way of resolving.
Or permanently tell the class that T conform to the protocol by declaring the class.
class A<T: Resource> {}

Array of structs conforming to protocol not recognized

I'm trying to create a method that takes an array of structs that conform to a protocol in Swift.
For the simplest example of this, I define an empty protocol and a method that takes an array of objects conforming to that protocol and just prints them
protocol SomeProtocol {}
func methodTakingProtocol(objects: [SomeProtocol]) {
// do something with the array of objects
print(objects)
}
When I try to feed this method an array of structs that conform to SomeProtocol, however, I get an error
struct SomeStruct: SomeProtocol {}
let arrayOfStructs = [ SomeStruct(), SomeStruct() ]
methodTakingProtocol(arrayOfStructs)
// ^ "Cannot convert value of type '[SomeStruct]' to expected argument type '[SomeProtocol]'"
Poking around a little, I've found that I can get around this problem by explicitly calling out SomeStruct's adoption of SomeProtocol
let arrayOfStructs: [SomeProtocol] = [ SomeStruct(), SomeStruct() ]
// This will work
methodTakingProtocol(arrayOfStructs)
Can someone tell me what's going on here? Is this a bug that I should file a radar for, or is there some reasoning as to why the compiler doesn't recognize this array of structs as conforming to the protocol they have been marked as adopting?
This is actually working as intended. In order to pass the array to the method you have to either cast it or explicitly declare it as the protocol:
protocol SomeProtocol {}
struct SomeStruct: SomeProtocol {}
// explicitly typed
let arrayOfStructs:[SomeProtocol] = [ SomeStruct(), SomeStruct() ]
func foo(bar:[SomeProtocol]) { }
foo(arrayOfStructs) // Works!
Here is an excellent article on this topic: Generic Protocols & Their Shortcomings
But that begs the question; Why can't we use generic protocols outside
of generic constraints?
The short answer is: Swift wants to be type-safe. Couple that with the
fact that it's an ahead-of-time compiled language, and you have a
language that NEEDS to be able to infer a concrete type at anytime
during compilation. I can't stress that enough. At compile time, every
one of your types that aren't function/class constraints need to be
concrete. Associated types within a protocol are abstract. Which means
they aren't concrete. They're fake. And no one likes a fake.
edit: It's still a great article but upon re-reading I realized that it doesn't exactly apply here since we're discussing "concrete protocols" and not "generic protocols".

In Swift, what does it mean for protocol to inherit from class keyword?

In Swift, what does it mean for protocol to inherit from class keyword?
e.g.
protocol MyDelegate: class {
}
The gist of Starscream's answer is correct, but it misses the why which I think is important here. It comes down to ARC and memory management.
Swift is a language of reference types and value types. Classes are reference types, while everything else is a value type. Effectively, we're not really specifying that the protocol inherits from class... it's more like we're specifying that the protocol can only be implemented by reference types.
And why is that important?
It's important, because without it, we can't use the weak keyword with the protocol.
protocol ExampleProtocol {}
class DelegatedClass {
weak var delegate: ExampleProtocol?
}
This generates the error:
'weak' cannot be applied to non-class type 'ExampleProtocol'
And why not? Because the weak keyword only makes sense with reference types to which ARC applies. ARC does not apply to value types. And without specifying our protocol with class, we cannot guarantee that our delegate property is set to a reference-type. (And if we don't use weak, we're most likely creating a retain cycle.)
From the Apple docs:
You can limit protocol adoption to class types (and not structures or
enumerations) by adding the class keyword to a protocol’s inheritance
list.
Example:
protocol AProtocol: class {
}
//Following line will produce error: Non-class type 'aStruct' cannot conform to class protocol 'AProtocol'
struct aStruct: AProtocol {
}
The line declaring the structure will spit an error. Following line will produce error:
Non-class type 'aStruct' cannot conform to class protocol 'AProtocol'