Let me provide some examples from the projects I was working on:
- AnyEncodable
extension URLRequest {
mutating func encoded(encodable: Encodable, encoder: JSONEncoder = JSONEncoder()) throws -> URLRequest {
httpBody = try encoder.encode(encodable) // Error: Protocol 'Encodable' as a type cannot conform to the protocol itself
}
}
Here we need AnyEncodable type erasure struct. It will be good if existentials become self conforming, as Error protocol do.
- AnalyticsSender
public protocol StatisticsSender: AnyObject {
func sendEvent(_ event: StatisticsEvent)
... other functions and properties
}
public protocol StatisticsSDKSender: StatisticsSender {
// Different SDKs use different types as value Type of event payload: [String: NSObject] / [String: Any] / [String: String]. So NativeValue becomes NSObject, Any or String in implementation.
associatedtype NativeValue
func stringRepresentation(ofEventName eventName: StatisticsEventName) -> String?
... other functions and properties
}
Currently we have to split protocol in two different, because we can not create Array<StatisticsSDKSender>
.
Another option is to create type erasure struct. Honestly, I like none of them. Array<StatisticsSDKSender>
would be great. What we need is:
- keep strong references to senders in array.
- be able to call methods, that don't depend on associated value (NativeValue in provided example). Such as func stringRepresentation(ofEventName:) -> String
-
AnyCollection<StatisticsEventParam>
Analytics event has a property with params. Now its type is AnyCollection.
We don't use Array, because it is not suitable. Fo example, several weeks ago we began to use OrderedSet from SwiftCollections library.
Opaque types don't help here, because the don't provide element type. At least in the way they work now
Here we need something like this:
var params: Collection where Element == StatisticsEventParam { get }
// or ideal variant
var params: some Collection where Element == StatisticsEventParam { get }
-
AnyRouter
Situation is equal to StatisticsSDKSender. Several protocols instead of one.
-
AnyObserver
public struct AnyObserver: ObserverType {}
Good if we are able to create collection of ObserverType:
class MainViewModel {
var observers: Set<ObserverType> where Element == Response
}
- AnyViewModelConfigurable
protocol AnyViewModelConfigurable: ViewModelConfigurable {
associatedtype ViewModel
func configure(with viewModel: ViewModel)
}
Situation is equal to StatisticsSDKSender. Several protocols instead of one.
- AnyEquatable
It is interesting case.
@Joe_Groff says: "different dynamic types can be compared by wrapping both in AnyHashable"
From my point of view comparing two instances of different types via wrapping them by AnyHashable is language defect.
Opaque types provide this in type safe manner.
If we create Array, then put there String, Int and say, CLLocation, then pass it somewhere and become to compare, it is very strange.
"Equatable
and Hashable
existentials ought to be something we handle as a special case" - I can hardly imagine reasons for this. It will be interesting to know them.
Seems we can make both variants possible.
-
Equatable
and Hashable
behave as all other PAT protocols
- AnyHashable struct continues to live in Standard Library. Those, who want to compare different types, can wrap them in AnyHashable.