Is it possible to construct a collection (array, dictionary, set) type checks values against both a class and a protocol? Given:
class Piece {
var name: String?
}
protocol Jump {
func jump() { ... }
}
protocol Move {
func move() { ... }
}
Allows:
var pieces: [Piece]?
Or:
var moves: [Move]?
var jumps: [Jump]?
Or:
var movenjump: [protocol <Move,Jump>]
But, I'm not sure how to restrict a collection to instances of Piece that Move and Jump.
The short answer is no. To date, Swift only allow collections for either a single Class type or protocol types (single protocol or multiple protocols). But there are ways you can workaround this.
The best approach is if Piece is your code, try to make it a protocol. Then you can declare array for example: let anArray: [protocol<Piece, Move, Jump>].
If you don't have access to the source code of Piece, try to generify the surrounding class which needs to declare the collection. This way, you can use where clause to constrain the generic type to anything you like:
class MyClass<T where T: Piece, T: Move, T: Jump> {
var myCollection: [T]?
}
Related
I’m trying to create a struct which acts as a storage for the results being returned from a web API. This API returns different JSON results which are modeled as a set of structs.
These results need to be stored in an array inside of a storage class which will need to be generic as it should be able to store arrays of any of the types returned. I’m struggling, however, with adding generic data to an array… and this is where you guys might come in.
This is the storage class:
class FooStorage<F: Fooable> {
private var storage: [F] = []
func add<F: Fooable>(_ someFoo: F) {
storage.append(someFoo)
}
}
These are two sample structs modeling what the API mentioned will return:
struct FooA: Fooable, Decodable {
var foo: String
}
struct FooB: Fooable, Decodable {
var foo: String
var bar: String
}
And finally, this is the protocol I created in order to specify that all of these structs are results of the same API:
protocol Fooable {}
The compiler error I get is this:
No exact matches in call to instance method append
And it is thrown on the storage.append(_:) method of the FooStorage class. Tried to add Equatable and Hashable conformance to the FooX protocols but to no avail. Seems I need some enlightenment here… thanks in advance!
In your add function, remove the generic part & let it use the class generic:
class FooStorage<F: Fooable> {
private var storage: [F] = []
func add(_ someFoo: F) {
storage.append(someFoo)
}
}
I am thinking on this for hours:
Lets say you have a generic game. The game can have players and an methode that sets a score. This can be done by using a protocol:
protocol Game {
var players: [Player] {get set}
func setScore (for player: Player, value: Int)
func sendSomeMessage(message: String)
}
i can now create a struct confirming to the protocol "Game"
struct raiseGame: Game {
var players: [Player]()
func setScore (for player: Player, value: Int) {
player.score += value
}
func sendSomeMessage(message: String) {
players.forEach { player in
//send the message to each player
}
}
}
But then i have, lets say, another struct conforming to Game. But in this time the score should be decreased. All other things stay the same. Now i have to write all the code for the func sendSomeMessage again. (There could be many more functions which stays the same).
The question is now: Should i switch to a class, write a base class and inherit from it, or is there a way to provide somekind of default implementations for functions in protocols, so i don't need to write them every time again, when confirming to a protocol?
is there a way to provide somekind of default implementations for functions in protocols, so i don't need to write them every time again, when confirming to a protocol?
There is indeed a way to provide default method implementations for protocol-conforming types:
protocol MyProtocol {
func myMethodRequirement()
}
extension MyProtocol {
func myMethodRequirement() {
// Default implementation.
}
}
struct Foo: MyProtocol {
func myMethodRequirement() {
// Foo-specific implementation.
}
}
struct Bar: MyProtocol {
/* Inherits default implementation instead. */
}
In your case, giving sendSomeMessage(message:) a default implementation would look like:
extension Game {
func sendSomeMessage(message: String) {
// Default implementation here.
}
}
Any type conforming to Game could either implement sendSomeMessage(message:) itself (similar to override-ing a method in a class), or use the default implementation without doing any additional work.
For more information, you can see the "Providing Default Implementations" section on protocols of the The Swift Programming Language guide.
To answer the title of your question, then, given that it is possible to do this for structs:
A class may be useful over a struct if you have an inheritance hierarchy multiple levels deep, which may be more complicated to express using protocol conformances
A class may be useful over a struct when you need reference semantics — see:
Structures and Enumerations are Value Types and
Classes are Reference Types
In your specific case, it doesn't immediately appear that either of these apply, but with more information, it might become obvious whether switching would be beneficial or not.
I have a protocol where I want to express that a function/variable can return a RandomAccessCollection of a specific type - not necessarily an Array because for one implementation it uses a library to access the data. It's easy to wrap the library calls in a RandomAccessCollection-conforming class, so I'd rather not have to construct an Array which would involve a bunch of extra copying.
I'm trying something like this:
protocol MyThing
{
var entries: RandomAccessCollection where Element: MyEntry { get }
}
..but the compiler doesn't like that; it doesn't seem to like having a where clause there.
Is there a way to do this, such that one implementation of my protocol can return a custom RandomAccessCollection-conforming class, and another (say, a mock version for testing) can return an Array? Or will I need to define a RandomAccessCollection for all cases?
Swift 4
Swift 4 implements the following evolution proposal:
SE-0142: Permit where clauses to constrain associated types
which will allow you to add an associatedtype to your protocol that can make use of more sophisticated type constraints via a where clause; adding constraints not only to the associatedtype type itself. E.g., as applied to your example:
// Swift 4 and beyond
protocol MyEntry { /* ... */ }
protocol MyThing {
associatedtype MyCollectionType: RandomAccessCollection
where MyCollectionType.Iterator.Element: MyEntry
var entries: MyCollectionType { get }
}
extension Int : MyEntry { /* ... */ }
// OK, Int conforms to MyEntry
struct Foo: MyThing {
internal var entries: [Int]
}
// Compile time error: Double doesn't conform to MyEntry
struct Bar: MyThing {
internal var entries: [Double]
}
// OK given that MyCustomRandomAccessCollection conforms
// to RandomAccessCollection
struct Baz: MyThing {
internal var entries: MyCustomRandomAccessCollection<Int>
}
I want to define a protocol that defines a variable "sequence" that is of type "Collection". I want that, because i have several conforming types, that will all hold an array of different types. Example:
protocol BioSequence {
associatedtype T
var sequence: Collection { get }
// Will also implement conformance to Collection and redirect all Collection functions to the property "sequence". This is because my types that conform to BioSequence are just wrappers for an array of Nucleotide or AminoAcid with some extra functionality
}
struct DNASequence: BioSequence {
// Will hold "sequence" of type [Nucleotide]
}
struct AminoSequence: BioSequence {
// Will hold "sequence" of type [AminoAcid]
}
Why do i want this? Because i need to implement the conformance to "Collection" only once in BioSequence and all conforming type inherit it automatically. Plus i can freely add extra functionality on the conforming types.
Now, when i try this like the code above, the compiler says: "Protocol Collection can only be used as a generic constraint". Yes, i googled what this error means, but how can i actually fix it to make my code work, like i want. Or is it not even possible to do what i want?
Thank you.
You can easily achieve this by using an associatedtype in your protocol, which can be constrained to Collection, allowing conforming types to satisfy the requirement with a concrete type when they adopt the protocol.
For example:
protocol CollectionWrapper : Collection {
associatedtype Base : Collection
var base: Base { get }
}
extension CollectionWrapper {
var startIndex: Base.Index {
return base.startIndex
}
var endIndex: Base.Index {
return base.endIndex
}
func index(after i: Base.Index) -> Base.Index {
return base.index(after: i)
}
subscript(index: Base.Index) -> Base.Iterator.Element {
return base[index]
}
// Note that Collection has default implementations for the rest of the
// requirements. You may want to explicitly implement them and forward them to
// the base collection though, as it's possible the base collection implements
// them in a more efficient manner (e.g being non random access and having
// a stored count).
}
// S adopts CollectionWrapper and satisfies the 'Base' associatedtype with [String].
struct S: CollectionWrapper {
var base: [String]
}
let s = S(base: ["foo", "bar", "baz"])
print(s[1]) // "bar"
Regarding your comment:
If I want to use this like that: let a: CollectionWrapper = S() [...] this leaves me with "Protocol can only be used as a generic constraint" again.
The problem is that you cannot currently talk in terms of a protocol with associated type requirements, as the types which are used to satisfy those requirements are unknown to the compiler (this isn't a technical limitation though). You can solve this by using Swift's AnyCollection type eraser to wrap an arbitrary Collection with a given element type.
For example:
let a = AnyCollection(S(base: ["foo", "bar", "baz"]))
If you need to work with additional protocol requirements from CollectionWrapper, you'll have to implement your own type eraser to do so. See Rob's answer here for an idea of how to go about this.
I'm trying to use Protocol-Oriented Pgrogramming for model layer in my application.
I've started with defining two protocols:
protocol ParseConvertible {
func toParseObject() -> PFObject?
}
protocol HealthKitInitializable {
init?(sample: HKSample)
}
And after implementing first model which conforms to both I've noticed that another model will be basically similar so I wanted to create protocol inheritance with new one:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
var value: AnyObject { get set }
}
A you can see this protocol has one additional thing which is value but I want this value to be type independent... Right now I have models which use Double but who knows what may show up in future. If I leave this with AnyObject I'm sentenced to casting everything I want to use it and if I declare it as Double there's no sense in calling this BasicModel but rather BasicDoubleModel or similar.
Do you have some hints how to achieve this? Or maybe I'm trying to solve this the wrong way?
You probably want to define a protocol with an "associated type",
this is roughly similar to generic types.
From "Associated Types" in the Swift book:
When defining a protocol, it is sometimes useful to declare one or
more associated types as part of the protocol’s definition. An
associated type gives a placeholder name (or alias) to a type that is
used as part of the protocol. The actual type to use for that
associated type is not specified until the protocol is adopted.
Associated types are specified with the typealias keyword.
In your case:
protocol BasicModel: HealthKitInitializable, ParseConvertible {
typealias ValueType
var value: ValueType { get set }
}
Then classes with different types for the value property can
conform to the protocol:
class A : BasicModel {
var value : Int
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
class B : BasicModel {
var value : Double
func toParseObject() -> PFObject? { ... }
required init?(sample: HKSample) { ... }
}
For Swift 2.2/Xcode 7.3 and later, replace typealias in the
protocol definition by associatedtype.