Default Swift protocol implementation in extension for class that conforms to other protocol - swift

I have a class where some subclasses conform to Protocol1. The API from Protocol1 is sufficient to implement Protocol2. I want to do something like below, where I can define a default implementation of Protocol2 when an instance of my class conforms to Protocol1. My first attempt looks like this:
public class MyClass {}
public protocol Protocol1 {
var someProperty: Any { get }
}
public protocol Protocol2 {
var func someFunc()
}
extension Protocol2 where Self: MyClass & Protocol1 {
public func someFunc() {
// Functionality that uses `.someProperty`
}
}
This compiles fine, so I thought it would work. However I tried testing with something like:
public class MySubclass: MyClass {}
extension MySubclass: Protocol1 {
public var someProperty: Any {
// Return property value.
}
}
let instance = MySubclass()
instance.someFunc() // Throws compilation error
Unfortunately, I get a Value of type 'MySubclass' has no member 'someFunc' compilation error when attempting to call the function from Protocol2. Is there a way to define a default implementation of Protocol2 for MyClass subclasses conforming to Protocol1, or would I just need to extend each subclass individually?

Figured it out actually. You can define the default implementation with the where clause, but you still need to explicitly conform to Protocol2. I have several MyClass subclasses conforming to Protocol1, so I didn't want to duplicate the implementations, but just adding explicit protocol conformance to Protocol2 with an empty definition block works:
extension Protocol2 where Self: MyClass & Protocol1 {
public func someFunc() {
// Functionality that uses `.someProperty`
}
}
public class MySubclass: MyClass {}
extension MySubclass: Protocol1 {
public var someProperty: Any {
// Return property value.
}
}
extension MySubclass: Protocol2 {}
MySubclass().someFunc() // Works fine

Related

How to create a protocol with a generic function extending type

I'm trying to do a protocol with a generic function where T is not just equal to the type, but extends it.
class MainItem {}
class Item1: MainItem {}
class Item2: MainItem {}
protocol MyProtocol {
func myFunc<T: MainItem>() -> T // T extends MainItem
}
class ClassA: MyProtocol {
func myFunc() -> Item1 { // not MainItem
return Item1()
}
}
class ClassB: MyProtocol {
func myFunc() -> Item2 { // not MainItem
return Item2()
}
}
But I get this error
Type 'ClassA' does not conform to protocol 'MyProtocol'
because Item1 is not equal to MainItem (it expands it). How can you make it work?
For example, in Java everything can be done using abstract class:
abstract class MyProtocol {
abstract <T extends MainItem> T myFunc()
}
Generics is not the way to go for your requirements. When you declare a generic function in a protocol, the generic type parameter will mean that the same function works for all types that satisfy the generic type restriction, but the function signature still needs to be intact for all conforming types.
What you are looking for is a protocol with associated type. An associated type on a protocol means that the conforming type can decide what concrete type to use in place of the associated type, hence allowing you to use different associated types in different conforming classes.
protocol MyProtocol {
associatedtype MyType: MainItem
func myFunc() -> MyType
}
class ClassA: MyProtocol {
func myFunc() -> Item1 {
return Item1()
}
}
class ClassB: MyProtocol {
func myFunc() -> Item2 {
return Item2()
}
}

Protocol extensions not using most specific implementation

I have two classes, Object and SubObject. A protocol, MyProtocol, has an associatedtype of type Object. In two extensions, I have provided implementations of the save function. When I create an instance of TestClass with either of the classes, they both result in a call to the least specific extension implementation, while it would be expected to call the most specific one.
class Object {}
class SubObject: Object {}
protocol MyProtocol {
associatedtype T: Object
func save()
}
extension MyProtocol {
func save() {
print("Object")
}
}
extension MyProtocol where T == SubObject {
func save() {
print("SubObject")
}
}
class MyClass<T: Object>: MyProtocol {
}
class TestClass<T: Object> {
typealias U = MyClass<T>
func test() {
let myClass = U()
myClass.save()
}
}
let testClass1 = TestClass<Object>()
testClass1.test() // returns "Object"
let testClass2 = TestClass<SubObject>()
testClass2.test() // returns "Object" (should be "SubObject")
How can I solve this, so that the TestClass calls the correct implementation of save? Or is this not currently possible in Swift? Any help would be appreciated!

Swift protocol extension for DTO model [duplicate]

Why does the following code produce an error?
protocol ProtocolA {
var someProperty: ProtocolB { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA { // Type 'SomeClass' does not conform to protocol 'ProtocolA'
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
The answer in this similar question makes sense. However, in my example, the property is get-only. Why shouldn't this work? Is it a shortcoming of Swift, or is there some reason this makes sense?
There's no real reason why this shouldn't be possible, a read-only property requirement can be covariant, as returning a ConformsToB instance from a property typed as ProtocolB is perfectly legal.
Swift just currently doesn't support it. In order to do so, the compiler would have to generate a thunk between the protocol witness table and conforming implementation in order to perform the necessary type-conversion(s). For example, a ConformsToB instance would need to be boxed in an existential container in order to be typed as ProtocolB (and there's no way the caller can do this, as it might not know anything about the implementation being called).
But again, there's no reason why the compiler shouldn't be able to do this. There are multiple bug reports open over this, this one which is specific to read-only property requirements, and this general one, in which Slava Pestov, a member of the Swift team, says:
[...] we want protocol witnesses and method overrides in every case where a function conversion is allowed
So it definitely looks like something the Swift team are looking to implement in a future version of the language.
In the mean time however, as #BallpointBen says, one workaround is to use an associatedtype:
protocol ProtocolA {
// allow the conforming type to satisfy this with a concrete type
// that conforms to ProtocolB.
associatedtype SomeProperty : ProtocolB
var someProperty: SomeProperty { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// implicitly satisfy the associatedtype with ConformsToB.
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
But this is quite unsatisfactory, as it means that ProtocolA is no longer usable as a type (because it has associatedtype requirements). It also changes what the protocol says. Originally it said that someProperty could return anything that conformed to ProtocolB – now it says that an implementation of someProperty deals with just one specific concrete type that conforms to ProtocolB.
Another workaround is just to define a dummy property in order to satisfy the protocol requirement:
protocol ProtocolA {
var someProperty: ProtocolB { get }
}
protocol ProtocolB {}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// dummy property to satisfy protocol conformance.
var someProperty: ProtocolB {
return actualSomeProperty
}
// the *actual* implementation of someProperty.
var actualSomeProperty: ConformsToB
init(someProperty: ConformsToB) {
self.actualSomeProperty = someProperty
}
}
Here we're essentially writing the thunk for the compiler – but it's also not particularly nice as it adds a needless property to the API.
In addition to Harmish's great response, if you want to keep using the same property name on both SomeClass and ProtocolA, you can do
protocol ProtocolB {}
protocol ProtocolA {
var _someProperty_protocolA: ProtocolB { get }
}
extension ProtocolA {
var someProperty: ProtocolB {
return _someProperty_protocolA
}
}
class ConformsToB: ProtocolB {}
class SomeClass: ProtocolA {
// the *actual* implementation of someProperty.
var _someProperty: ConformsToB
var someProperty: ConformsToB {
// You can't expose someProperty directly as
// (SomeClass() as ProtocolA).someProperty would
// point to the getter in ProtocolA and loop
return _someProperty
}
// dummy property to satisfy protocol conformance.
var _someProperty_protocolA: ProtocolB {
return someProperty
}
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
}
let foo = SomeClass(someProperty: ConformsToB())
// foo.someProperty is a ConformsToB
// (foo as ProtocolA).someProperty is a ProtocolB
This can be useful when you are conforming to another protocol ProtocolA2 that would originally also have constraint on someProperty as well, or when you want to hide your hack around swift limitations.
I'm now curious to know why Swift is not doing this for me directly.
Beginning in Swift 5.1, you can use opaque return types to reference a protocol that references another protocol, so long as you also use associatedtypes to do so.
Not only does it work for readonly "get" properties, but also readwrite properties. For example,
protocol ProtocolA {
associatedtype T: ProtocolB
var someProperty: T { get }
var x: Int { get set }
}
protocol ProtocolB {
var x: Int { get set }
}
struct ConformsToB: ProtocolB {
var x: Int
}
class SomeClass: ProtocolA {
var someProperty: ConformsToB
init(someProperty: ConformsToB) {
self.someProperty = someProperty
}
var x: Int {
get {
someProperty.x
}
set {
someProperty.x = newValue
}
}
}
var protocolA: some ProtocolA = SomeClass(someProperty: ConformsToB(x: 1))
print(protocolA.x) // 1
protocolA.x = 2
print(protocolA.x) // 2

Swift protocol property in protocol - Candidate has non-matching type

I have a protocol (ProtocolA) containing a single property conforming to a second protocol (ProtocolB).
public protocol ProtocolA {
var prop: ProtocolB? { get }
}
public protocol ProtocolB {
}
I'm trying to declare two classes that will implement those:
private class ClassA : ProtocolA {
var prop: ClassB?
}
private class ClassB : ProtocolB {
}
But I get an error:
Type 'ClassA' does not conform to protocol 'ProtocolA'
Protocol requires property 'prop' with type 'ProtocolB?'
Candidate has non-matching type 'ClassB?'
Which is annoying as ClassB conforms to ProtocolB.
in the good-old i'd probably just declare the property as:
#property (nonatomic) ClassB <ProtocolB> *prop;
but the only way it seems I can get around this in swift is by adding an ivar like:
private class ClassA : ProtocolA {
var _prop: ClassB?
var prop: ProtocolB? { return _prop }
}
Is there no way around this?
You need to declare a typealias of the type that conforms to the other protocol. The way you did it is that prop has to be exactly of type ProtocolB, but you don't actually want that, you want a type that conforms to it instead.
protocol ProtocolA {
typealias Prop : ProtocolB
var prop: Prop? { get }
}
protocol ProtocolB {}
class ClassA : ProtocolA {
var prop: ClassB?
}
class ClassB : ProtocolB {}

How to have a variable with generic typing and multiple inheritance in Swift?

I have this Protocol, Class, and a Class that takes a generic which has to conform to both.
I want to build a registry which holds an array of such classes, but how do I define a variable in the registry which will satisfy the compiler?
Consider this example:
protocol SomeProtocol {
}
class SomeClass {
}
class AnotherClass<R, P where P:SomeProtocol, P:SomeClass> {
}
class Registry {
private init() {}
// This is not allowed since the second generic doesn't conform to neither required class
var registry:[AnotherClass<AnyObject, AnyObject>] = []
}
This is how
import UIKit
protocol SomeProtocol {
}
class SomeClass {
}
class AnotherClass<R, P where P:SomeProtocol, P:SomeClass> {
}
class TheClass : SomeClass, SomeProtocol {}
class Registry {
private init() {}
var registry:[AnotherClass<AnyObject, TheClass>] = []
}
You need to define a class that inherits SomeClass and SomeProtocol. AnyObject doesn't inherit from SomeClass and SomeProtocol and that's why you get an error.
Update
To make it less restritive
This is how
import UIKit
protocol SomeProtocol {
}
class SomeClass {
}
class AnotherClass<R, P where P:SomeProtocol, P:SomeClass> {
}
class TheClass : SomeClass, SomeProtocol {}
class Registry<T, where T:SomeProtocol, T:SomeClass> {
private init() {}
var registry:[AnotherClass<AnyObject, T>] = []
}
Update 2
Eventually, however, you will end up with something like
Registry<TheClass>
because generics needs a concrete class in the end.
What you could do is enforce the type at runtime instead - you will need to add #objc to your protocols though - via is. An alternative would be to scrap generics and use a interface or a base class.