Swift Associated type constraints - swift

I have two protocols with each defining an associated type. One of the protocols needs to define a variable of typed the other protocol where they both have the same type for associated type. Is it possible to somehow infer the type of associated type?
protocol A {
associatedtype AModel
var b: B { get }
}
protocol B {
associatedtype BModel
func doAnother(anotherModel: BModel)
}
Here is what I tried with no success
protocol A {
associatedtype AModel
associatedtype TypedB = B where B.BModel == AModel
var another: TypedB { get }
}
protocol B {
associatedtype BModel
func doAnother(anotherModel: BModel)
}

Please find the following working playground example. You need to use the associated type's name, not the constraining protocol's name. The reason for this is described here.
import Foundation
protocol A {
associatedtype AModel
associatedtype TypedB: B where TypedB.BModel == AModel
var another: TypedB { get }
}
protocol B {
associatedtype BModel
func doAnother(anotherModel: BModel)
}
// compiles
struct One: B {
typealias BModel = String
func doAnother(anotherModel: String) {}
}
struct Second: A {
typealias AModel = String
typealias TypedB = One
var another: One
}
// does not compile
struct Third: B {
typealias BModel = Int
func doAnother(anotherModel: Int) {}
}
struct Fourth: A { // A' requires the types 'Fourth.AModel' (aka 'String') and 'Third.BModel' (aka 'Int') be equivalent
typealias AModel = String
typealias TypedB = Third
var another: Third
}

Related

Create an array of protocols with constrained associated types

This is a basic example of creating an array of protocols with associated types using type erasure:
protocol ProtocolA {
associatedtype T
func doSomething() -> T
}
struct AnyProtocolA<T>: ProtocolA {
private let _doSomething: (() -> T)
init<U: ProtocolA>(someProtocolA: U) where U.T == T {
_doSomething = someProtocolA.doSomething
}
func doSomething() -> T {
return _doSomething()
}
}
Creating an array of them isn't hard:
let x: [AnyProtocolA<Any>] = []
Is there any way way I can create an array of protocols which have associated types that are constrained? This is what I have tried:
protocol Validateable {
// I removed the functions and properties here to omit unreleveant code.
}
protocol ProtocolA {
associatedtype T: Validateable
func doSomething() -> T
}
struct AnyProtocolA<T: Validateable>: ProtocolA {
private let _doSomething: (() -> T)
init<U: ProtocolA>(someProtocolA: U) where U.T == T {
_doSomething = someProtocolA.doSomething
}
func doSomething() -> T {
return _doSomething()
}
}
It compiles! But didn't it defeated the chance of creating an array of AnyProtocolA's now? Now I can't use type Any as a placeholder in my array.
How do I create an array of AnyProtocolA's which has a constrained associated type? Is it even possible? This won't work since Any ofcourse doesn't conform to Validateable:
let x: [AnyProtocolA<Any>] = []
Extending Any can't be done:
extension Any: Validateable {} // Non nominal type error
Edit:
I think I already found it, just type erasure the protocol Validateable as well:
protocol Validateable {
// I removed the functions and properties here to omit unreleveant code.
}
protocol ProtocolA {
associatedtype T: Validateable
func doSomething() -> T
}
struct AnyProtocolA<T: Validateable>: ProtocolA {
private let _doSomething: (() -> T)
init<U: ProtocolA>(someProtocolA: U) where U.T == T {
_doSomething = someProtocolA.doSomething
}
func doSomething() -> T {
return _doSomething()
}
}
struct AnyValidateable<T>: Validateable {}
Now I can use it as:
let x: [AnyProtocolA<AnyValidateable<Any>>] = []
Any answers that are better are always welcome :)

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?
}

Swift - How to get class that extended from protocol?

Is there a way that I could get the Class type or Struct type that extended my Protocol?
Here are my sample code:
protocol a {}
extension a {
static func list(completion: ([StructType] -> Void)) {
var items = [StructType]()
...
completion(items)
}
}
struct b{}
extension b: a {}
struct c{}
extension c: a{}
In this case I want to dynamically get the type of struct a and b, so that I could generate a list of it and return.
Thank you in advance for kindly answering my question.
Use the Self keyword
protocol P {
init()
}
extension P {
static func list(completion: ([Self]) -> Void) {
let items = [Self(), Self(), Self()]
print(Self.self)
completion(items)
}
}
struct B {}
extension B: P {}
class C {
required init() {}
}
extension C: P {}
B.list{ print($0) }
C.list{ print($0) }

why is this causing so much trouble? (protocols and typealiases on their associated types)

I'm trying to do something along the lines of this:
protocol Protocol {
associatedtype T
associatedtype ArrayT = Array<T>
}
struct Struct<ProtocolType: Protocol> {
func doSomething(with: ProtocolType.ArrayT) {
let _ = with.map { $0 }
// ^ compiler complains on this line
// "value of type ProtocolType.ArrayT has no member map"
}
}
where I define a convenience typealias ArrayT that uses the associatedtype T. It seems that when I try to use ArrayT like in doSomething(_:), I lose the Array type information of ArrayT.
Shouldn't ArrayT definitely be an Array and therefore a member of the Sequence protocol, exposing the map function? 🤔
the working solution I'm employing now is to just define a generic typealias outside of the protocol:
typealias ProtocolArray<ProtocolType: Protocol> = Array<ProtocolType.T>
struct Struct<ProtocolType: Protocol> {
func doSomething(with: ProtocolArray<ProtocolType>) {
let _ = with.map { $0 } // no complaints
}
}
what am I missing here?
The line associatedtype ArrayT = Array<T> only tells the compiler that the default value of ArrayT is Array<T>. An adaption of the protocol can still change ArrayT like:
struct U: Protocol {
typealias T = UInt32
typealias ArrayT = UInt64 // <-- valid!
}
If you want a fixed type, you should use a typealias...
// does not work yet.
protocol Protocol {
associatedtype T
typealias ArrayT = Array<T>
}
But the compiler complains that the type is too complex 🤷. So the best you could do is constrain the ArrayT to be a Sequence / Collection / etc, and hope that the adaptors won't change the type themselves.
// still somewhat off
protocol Protocol {
associatedtype T
associatedtype ArrayT: Sequence = [T]
}
Note that, however, the Sequence can have any element type, but we want ArrayT's Element must be T. We cannot attach a where clause to the associatedtype:
// fail to compile: 'where' clause cannot be attached to an associated type declaration
associatedtype ArrayT: Sequence where Iterator.Element == T = [T]
Instead, you need to put this constraint every time you use the protocol:
struct Struct<ProtocolType: Protocol>
where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T {
Complete, working code:
protocol Protocol {
associatedtype T
associatedtype ArrayT: Sequence = [T]
// ^~~~~~~~~~
}
struct Struct<ProtocolType: Protocol>
where ProtocolType.ArrayT.Iterator.Element == ProtocolType.T
// ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
func doSomething(w: ProtocolType.ArrayT) {
let _: [ProtocolType.T] = w.map { $0 }
}
}
When you "initialize" an associatedtype, you're not defining it. That's not the point of associated types in the first place. Associated types are deliberately unbound ("placeholders") until the adopting class resolves them all. All you're doing there is giving it a default value, which a conforming class is allowed to override. To extend your example:
protocol Protocol {
associatedtype T
associatedtype ArrayT = Array<Self.T>
func useSomeT(myFavoriteT: T)
func useSomeArrayT(myLeastFavoriteArrayT: ArrayT)
}
class Whatever: Protocol {
func useSomeT(myFavoriteT: Int) {
print("My Favorite Int: \(myFavoriteT)")
}
func useSomeArrayT(myLeastFavoriteArrayT: [Int: String]) {
print(myLeastFavoriteArrayT.map { $0.1 })
}
}
struct Struct<ProtocolType: Protocol> {
func doSomething(stuff: ProtocolType.ArrayT) {
print(type(of: stuff))
}
}
let x = Struct<Whatever>()
x.doSomething(stuff: [3: "Doggies"])
For your example, it seems all you really want is to declare with: Array<ProtocolType.T> (or simply with: [ProtocolType.T]) as the parameter type.

How to construct generic without protocol in Swift?

I'm getting the following error...
'T' cannot be constructed because it has no accessible initializers
When compiling...
class Sub<T : Equitable> {
func def(v : T) -> Bool{
var d = T() // <- Error
return d == v
}
}
var s = Sub<Int>()
println(s.def(0), s.def(1)) // I'm expecting "true, false"
I understand that in order for a generic type to be initialized, it needs to conform to a protocol that contains an init() constructor. Such as...
protocol A : Equitable {
init()
}
class Sub<T : A> {
But then I would get get the error
Type 'Int' does not conform to protocol 'A'
at the line
var s = Sub<Int>()
So how would I go about making a value type such as Int or Bool conform to a protocol that can be initialized?
You need to extend Int like so for it to adopt protocol A:
class Sub<T : A> {
func def(v : T) -> Bool{
var d = T()
return d == v
}
}
protocol A : Equatable {
init()
}
extension Int: A {
}
var s = Sub<Int>()
s.def(0) // true
s.def(1)) // false