fcy
(Felipe Cypriano)
1
Given this enum which represents the response from a network call:
public enum Response<T: Model> {
case success(T)
case failure(Error)
}
It can represent a response with a single Model. What I'm wondering is: is there is a way to constraint T so it can be either a single Model or an array Model?
public enum Response<T: Model || [Model]> {
moiseev
(Max Moiseev)
2
If Model is a protocol, then one could utilize conditional conformances as follows:
extension Collection: Model
where Element : Model {
// Model conformance here
}
This way [Model] would satisfy the T : Model requirement.
fcy
(Felipe Cypriano)
3
Interesting idea, that would likely work for me. But the compiler wasn't into it 
Extension of protocol 'Collection' cannot have an inheritance clause
moiseev
(Max Moiseev)
4
Right. It's only available in ToT compiler, and only under a special flag, IIRC.
The next best thing I can think of is wrapping the array of models into a trivial struct and make this struct conform to the protocol. Should be essentially the same for a small price, that is, you'd have to instantiate this new struct manually.
Karl
(👑🦆)
5
Sorry, you can't make protocols conditionally-conform to other protocols. They have to refine them in the original declaration :(
saagarjha
(Saagar Jha)
6
Not yet, anyways. Wait for SE-0143 to be implemented.
xwu
(Xiaodi Wu)
7
...?
SE-0134 is my proposal titled "Rename two UTF8-related properties on String."
1 Like
saagarjha
(Saagar Jha)
8
I really shouldn't be typing on mobile. Thanks for catching that!
Karl
(👑🦆)
9
Even then, you won't be able to. It was explicitly ruled out.
Extending protocols to conform to protocols
The most common request related to conditional conformances is to allow a (constrained) protocol extension to declare conformance to a protocol. For example:
extension Collection: Equatable where Iterator.Element: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
// ...
}
}
This protocol extension would make any Collection of Equatable elements Equatable, which is a powerful feature that could be put to good use. Introducing conditional conformances for protocol extensions would exacerbate the problem of overlapping conformances, because it would be unreasonable to say that the existence of the above protocol extension means that no type that conforms to Collection could declare its own conformance to Equatable, conditional or otherwise.
You could add a conformance to the concrete Array type, though.
extension Array: Model where Element: Model {
// ...
}
4 Likes
cbarrett
(Colin Barrett)
10
As others have suggested until Swift's conformances are more advanced, you'll need to wrap this in a struct.
struct ModelCollection<C: Collection>: Model where C.Element: Model {
let value: C
}
Works OK for me. I tested by making an empty Model protocol and making Int conform to it; ModelCollection(value: [0]) type checked OK.
I'm assuming this doesn't fix your problem, but this is how I see it:
You want to accept either a Model instance (exactly one), or an array of Model instances (between 0...n).
So the case of accepting exactly one instance is actually a subset of accepting an array anyway.
Maybe there is a semantic difference in your case between receiving a Model instance and receiving a [Model] instance that contains exactly one element. But if not, I'd simplify to:
public enum Response<T: Model> {
case success([T])
case failure(Error)
}
fcy
(Felipe Cypriano)
12
I'll end up doing the everything is an array approach because I'm having too much trouble otherwise. I just wanted the caller to receive exactly what he wanted and I was wondering if it would be possible.
For future reference, this was my last attempt before deciding to go with the array everywhere approach:
public protocol Service {
func get<T>(path: String, parameters: [String: Any], completion: @escaping (Response <T>) -> Void) where T: Model
func get<T: Collection>(path: String, parameters: [String: Any], completion: @escaping (Response <T>) -> Void) where T.Element: Model
}
public enum Response<T> {
case success(T)
case failure(Error)
}
I was liking it at first but it started to require having "duplicated" methods everywhere.
moiseev
(Max Moiseev)
13
One other option would be:
enum ModelEnum {
case one(model: ModelProtocol)
case many(models: [ModelProtocol])
}
extension ModelEnum : ModelProtocol { ... }
It is somewhat similar to the "wrap an array into a struct" approach, just more clearly states that only these two cases are possible.
1 Like