How do you structure generic type protocol conformance in Swift? - swift

The following contrived Swift 2 example from real-world code won't compile:
protocol SomeModelType { }
protocol SomeProtocol {
var someVar: SomeModelType? { get }
}
class ConcreteClass<T: SomeModelType>: SomeProtocol {
var someVar: T?
}
This doesn't make sense to me fully. I would assume in ConcreteClass that because I have T being constrained to SomeModelType and have T as the backing type for the someVar property, the compiler would be able to figure out that the SomeProtocol was being conformed to by ConcreteClass.
How should an example like this be structured? Is it possible to the Swift compiler to determine protocol conformance through generic type constraints?

protocol SomeModelType { }
protocol SomeProtocol {
associatedtype T: Any
var someVar: T? { get }
}
class ConcreteClass<T> :SomeProtocol where T: SomeModelType {
var someVar: T?
}

Related

Protocol restriction on another protocol associated type

I have a protocol MyProtocol which has an associatedtype, inferred by the return type of myFunction
struct MyStruct: Codable {
var myVar: Int
}
protocol MyProtocol {
associatedtype MyType
func myFunction()-> MyType
}
class MyClass1: MyProtocol {
func myFunction()-> MyStruct? {
return MyStruct(myVar: 1) //ex.
}
}
class MyClass2: MyProtocol {
func myFunction()-> Int? {
return 1 // ex.
}
}
In MyClass1, associatedtype is infered as MyStruct?
In MyClass2, associatedtype is infered as Int?
from there everything works fine. No I want to build another protocol that can only be applied to a MyProtocol if associatedtype is Codable :
protocol MyProtocolCodable where Self: MyProtocol, MyType == Codable? {}
the code runs fine until here but when I try to apply it to my class I get an error:
extension MyClass1: MyProtocolCodable{} 🛑
'MyProtocolCodable' requires the types 'MyStruct?' and 'Codable?' (aka 'Optional<Decodable & Encodable>') be equivalent
yet, as far as I can see MyStruct? (aka Optional) and Codable? (aka Optional<Decodable & Encodable>) are equivalent?
How can I get rid of this error message? Am I doing something that is not meant to be done?
As far as I can see MyStruct? (aka Optional) and Codable? (aka
Optional<Decodable & Encodable>) are equivalent?
No, they are not. MyStruct conforms to Codable but is not equivalent of it.
As in: every MyStruct is Codable but not every Codable is MyStruct.
You can try changing MyType == Codable? to MyType: Codable:
protocol MyProtocolCodable where Self: MyProtocol, MyType: Codable {}
as MyStruct is not equal to Codable?.

Class-only protocol as typealias for associatedtype with AnyObject constraints

In Swift 4.0 I could write something like this
protocol ObserversHolder {
///Compiling Error in Swift 4.1
///note: possibly intended match 'StringManager.ObserverValue' (aka 'StringObserver') does not conform to 'AnyObject'
///note: protocol requires nested type 'ObserverValue'; do you want to add it?
associatedtype ObserverValue: AnyObject
var observers: [ObserverValue] {get set}
}
protocol StringObserver: class {
func showString()
}
class StringManager: ObserversHolder {
typealias ObserverValue = StringObserver
var observers = [ObserverValue]()
}
But in Swift 4.1 I receive the error Type 'StringManager' does not conform to protocol 'ObserversHolder'.
Is it possible to resolve this?
Change AnyObject to Any
protocol ObserversHolder {
///Compiling Error in Swift 4.1
///note: possibly intended match 'StringManager.ObserverValue' (aka 'StringObserver') does not conform to 'AnyObject'
///note: protocol requires nested type 'ObserverValue'; do you want to add it?
associatedtype ObserverValue: Any
var observers: [ObserverValue] {get set}
}
protocol StringObserver: class {
func showString()
}
class StringManager: ObserversHolder {
typealias ObserverValue = StringObserver
var observers = [ObserverValue]()
}

Swift optional protocol requirements

I have the following protocols declared.
protocol TypeAProtocol {
...
}
protocol TypeBProtocol {
...
}
protocol SomeProtocol {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
var objA: TypeA? { get set }
var objB: TypeB? { get set }
}
Now if I want to create a class that implements SomeProtocol, I would have done this
class SomeClass: SomeProtocol {
var objA: ClassOfTypeAProtocol?
var objB: ClassOfTypeBProtocol?
}
The problem I am facing now is, I want to be able to create classes that implement SomeProtocol without var objB: TypeB? { get set }. I want objB to be optional. How can I achieve that?
In Objective-C you can declare an optional member (method/computed property) in a protocol.
In pure Swift this is not possible unless you use the #objc annotation (but in this case structs and enums won't be able to conform to your protocol so it's not the best way to go).
So, how can you solve your problem?
Let's split SomeProtocol into 3 protocols
These are your protocols
protocol TypeAProtocol { }
protocol TypeBProtocol { }
protocol SomeProtocol {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
var objA: TypeA? { get set }
var objB: TypeB? { get set }
}
Now we'll split SomeProtocol into 3 protocols
protocol SomeProtocolBase {
associatedtype TypeA: TypeAProtocol
associatedtype TypeB: TypeBProtocol
}
protocol SomeProtocolPartA: SomeProtocolBase {
var objA: TypeA? { get set }
}
protocol SomeProtocolPartB: SomeProtocolBase {
var objB: TypeB? { get set }
}
That's it
Now you can write
class ClassOfTypeAProtocol: TypeAProtocol { }
class ClassOfTypeBProtocol: TypeBProtocol { }
class SomeClass: SomeProtocolPartA {
typealias TypeA = ClassOfTypeAProtocol
typealias TypeB = ClassOfTypeBProtocol
var objA: ClassOfTypeAProtocol?
}

var in protocol as specific protocol

How to describe a variable in a protocol on swift
any type, but supports a specific protocol
Something like this
protocol MyProtocol: class {
var itemsList: AnyObject where Collection { get } // AnyObject supports a Collection protocol
}
Maybe you want:
protocol MyProtocol: class {
associatedtype T: Collection
var itemsList: T { get }
}
If you want T to definitely be an object as well (not a struct) then you must wait for this proposal to make it into the language.
If you want a class to satisfy this protocol with T unspecified in the class's definition, make the class generic.
class C<T: Collection>: MyProtocol {
let itemsList: T
init(_ itemsList: T) {
self.itemsList = itemsList
}
}
let x = C([1,2,3])

Generic variables in protocols with associated types. Bug? [duplicate]

This question already has answers here:
Unable to use protocol as associatedtype in another protocol in Swift
(2 answers)
Closed 6 years ago.
I'm using Xcode 7.3 and Swift 2.3. I have difficulties using protocols with associated types that have variables. Look at the example:
protocol SomeProtocol {}
class SomeProtocolImpl: SomeProtocol {}
protocol ProtocolWithAssociatedType {
associatedtype T: SomeProtocol
var variable: T { get }
}
class TestClass: ProtocolWithAssociatedType {
var variable: SomeProtocol = SomeProtocolImpl()
}
For some reason, compiler shows errors:
How is that possible? Am I doing something wrong? Is it a bug? A known one?
What I've tried:
defined typealias for that associated type:
class TestClass: ProtocolWithAssociatedType {
typealias O = SomeProtocol
var variable: SomeProtocol = SomeProtocolImpl()
}
nope.
Used method instead:
protocol SomeProtocol {}
class SomeProtocolImpl: SomeProtocol {}
protocol ProtocolWithAssociatedType {
associatedtype T: SomeProtocol
func someMethod() -> T
}
class TestClass: ProtocolWithAssociatedType {
typealias T = SomeProtocol
func someMethod() -> SomeProtocol {
return SomeProtocolImpl()
}
}
just got different errors:
How should I create protocol with associated type and variable and avoid this errors?
The compiler needs a specific type for T that conforms to SomeProtocol.
While this line looks correct
var variable: SomeProtocol
the problem the compiler is running into is that SomeProtocol is not a type, it's a protocol.
The easiest solution, tell the compiler a type to use
var variable: SomeProtocolImpl = SomeProtocolImpl()
or just let it figure it out itself.
var variable = SomeProtocolImpl()