Swift: How to conform to protocol with associated types if I have two types to associate? - swift

OK weird question perhaps, and it's only because I don't really know what I'm asking for so I'll try to describe it best I can. Please direct me appropriately if this has been asked before.
So I'm using the awesome Codable protocol with pretty much all my models and I'm making a class that handles some storing, let's call it Storage<Model: Codable> which has a generic type conforming to Codable because one instance of this class will handle storage for one type of model.
Now I need to be notified when things change in the Storage instance, like stuff getting written to disk and deletions. So I make a new protocol StorageListener that declares functions like func storage(_ storage: Storage<CodableType>, didRemoveModelForKey key: String). Now since this uses the Storage type which requires use of a generic Model an associated type must be declared: associatedtype CodableType: Codable.
So I now use type erasure to make an AnyStorageListener<AnyCodableType: Codable>: StorageListener, that I can store in an array in my Storage class.
Now this is fine, I can just conform my ViewModel or whatever to StorageListener and declare the typealias CodableType = MyModel but what if I need my ViewModel to listen to two Storages of different types?
What I've come up with is using listener container objects that I can initialize with closures to the protocol functions and thus work around the problem. That should work but I was wondering if there's a cleaner solution? Perhaps some way to type erasure away the generic requirement altogether?

Related

How to make TypeAliases Hashable

I have the following definitions and a type alias:
`typealias Alien = AlienClass & AlienProtocol`
`class AlienClass: SKSpriteNode {}`
`protocol AlienProtocol {}`
In order to store an object in a Set (or Map) it needs to be Hashable. SKSpriteNode is hashable, so I have no problem with let s = Set<AlienClass>().
However, if I use let s = Set<AlienClass>() then I get the error Protocol 'Alien' (aka 'AlienClass & AlienProtocol') as a type cannot conform to 'Hashable'.
Is there any way to force the typealias to inherit the hash function from its underlying AlienClass (which itself inherits from SKSpriteNode)?
I ran into this same issue when trying to store protocols in Sets directly. I stumbled upon Type Erasure but this doesn’t seem to work for heterogenous objects in the set. My hacky workaround has been to store the objects implementing the interface in a map like so: var AlienProtocols: [SKSpriteNode: AlienProtocol] = [:]. This really isn't great though, so a better pattern would be helpful.

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

Constrain inherited associated type in a protocol

I would like to define a Swift protocol that is a also a sequence with an element that conforms to another, specific protocol. Ideally, I'd like to write something like this:
protocol A {
var container: Container { get }
}
protocol Container: Sequence where Iterator.Element: A {
...
}
Unfortunately, Swift doesn't currently allow where clauses in a protocol definition (and it also doesn't allow to use protocols with associated types as type constraints). Is there any way I can still do this? Containers (and ideally A's) should be able to have different backing implementation.
Edit: I know that I can move the sequence to a type-erased property of Container, which will solve all the problems at the expense of introducing unnecessary lexical overhead. Still, I'm interested in a solution which does not 'cheats' its way out of the type system.

Use of a Structure instead of a Class in Swift [duplicate]

This question already has answers here:
Why Choose Struct Over Class?
(17 answers)
Closed 7 years ago.
I learn Swift from some time, I know the differences between structure and class. The main difference is structure is of value type and class is of reference type but didn't understand when to use structure instead of a class. Please explain it.
For example, In case of Protocols:
First, We have just a protocol of struct type:
protocol SomeProtocol{
func doSomeStuff()
}
Second, We make protocol of class type like this:
protocol SomeProtocol: class{
func doSomeStuff()
}
So, Please explain me, when we have to use protocol of struct type or of class type.
Firstly structs are passed by value (copied), and a class is passed by reference (copied just the memory address to the object).You may want to use structs for simpler types, because you get a free init for all the properties your struct has.And with protocols, the first one you can use it on class,struct and enum, the second you say that you only use that on classes,and you may want to put class if your protocol is a delegate or a data source,because you want the property(of the type of your protocol) weak to avoid the memory cycle. IMHO use classes for multi-scene apps because you don't need to take care to update value when you edited something in an another scene.
The protocol is not "of struct type" or "of class type", that is wrong terminology.
If you write SomeProtocol: class you make sure only classes can conform to that protocol, structs cannot. If you don't include the class both classes and structs can conform.
The docs (scroll down to "Class-Only Protocols") tell you that
You can limit protocol adoption to class types (and not structures or enumerations) by adding the class keyword to a protocol’s inheritance list. The class keyword must always appear first in a protocol’s inheritance list, before any inherited protocols.
Use a class-only protocol when the behavior defined by that protocol’s requirements assumes or requires that a conforming type has reference semantics rather than value semantics. For more on reference and value semantics, see Structures and Enumerations Are Value Types and Classes Are Reference Types.