When to switch from struct to class in swift? - swift

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.

Related

Protocol Inheritance - overriding method parameters

I am trying to convert a design of mine to be pop. However I am stuck and already have bunch of threads for my approaches -not a duplicate of this question though- and apparently they are all dead end.
My question is, is there a way to override parameter types of a protocol method which inherited from another protocol?
struct Books: Codable {}
protocol Listener {
func listen(_ param: Codable)
}
protocol BooksListener: Listener {
func listen(_ param: Books)
}
class MyClass: BooksListener {
// I want only this one to required with the type.
func listen(_ param: Books) {
<#code#>
}
func listen(_ param: Codable) {
<#code#>
}
}
I did my research and I believe this is not how protocols work. I am just seeking a solution to this.
I tried to add a associatedType to the Listener and use it as the type inside listen(_:). But this solution restricts any class to have conformance to multiple protocols which inherits from Listener. Details can be found here
As a general rule, Swift protocols are not covariant. But your question is a special case, where, even if Swift allowed protocol covariance, your approach couldn't work.
In your example, a BooksListener is required to do everything a Listener can do. A Listener can take an arbitrary Codable as its parameter to listen. That means that a BooksListener must also be able to take an arbitrary Codable. Consider the following:
struct Books: Codable {}
protocol Listener {
func listen(_ param: Codable)
}
protocol BooksListener: Listener {
func listen(_ param: Books)
}
class MyClass: BooksListener {
// Illegal, but assuming you had what you're asking for
func listen(_ param: Books) {}
}
let l: Listener = MyClass()
l.listen("Strings are Codable, and Listener accepts Codable")
What would this do? This is violating LSP.
What you might want here is an associatedtype as you mention (but I'll explain later why you probably don't). With an associatedtype, you can define Listener the way I expect you mean.
struct Books: Codable {}
protocol Listener {
associatedtype Parameter: Codable
func listen(_ param: Parameter)
}
protocol BooksListener: Listener where Parameter == Books {
func listen(_ param: Books)
}
class MyClass: BooksListener {
func listen(_ param: Books) {}
}
Why don't you want this? Because it almost certainly doesn't mean anything. What can I possibly do with an arbitrary Listener? I can't call listen on it; I don't know the type of Parameter. There's no algorithm that it helps. And enabling algorithms is what associatedtypes are about.
But the real point is that "POP" doesn't mean "use lots of protocols" and absolutely doesn't mean "recreate class inheritance with protocols." It means "start with concrete types and extract protocols to share algorithms." So before you create a single protocol, you need to ask, what other kinds of Listeners exist in your program, and what algorithm needs to work on an arbitrary Listener? If there's no such algorithm, there should be no protocol.

Swift: Is it possible to add a protocol extension to a protocol?

Lets say I have two protocols:
protocol TheirPcol {}
protocol MyPcol {
func extraFunc()
}
What I want to do is to create a protocol extension for 'TheirPcol' which lets extraFunc() work on anything which conforms to 'TheirPcol'. So something like this:
extension TheirPcol : MyPcol { // Error 'Extension of protocol 'TheirPcol' cannot have an inheritance clause.
func extraFunc() { /* do magic */}
}
struct TheirStruct:TheirPcol {}
let inst = TheirStruct()
inst.extraFunc()
The kicker in this is that 'TheirPcol', 'TheirStruct' are all handled by an external API which I do not control. So I'm passed the instance 'inst'.
Can this be done? Or am I going to have to do something like this:
struct TheirStruct:TheirPcol {}
let inst = TheirStruct() as! MyPcol
inst.extraFunc()
It seems there are two use-cases of why you may want to do what you are doing. In the first use-case, Swift will allow you to do what you want, but not very cleanly in the second use-case. I'm guessing you fall into the second category, but I'll go through both.
Extending the functionality of TheirPcol
One reason why you might want to do this is simply to give extra functionality to TheirPcol. Just like the compiler error says, you cannot extend Swift protocols to conform to other protocols. However, you can simply extend TheirPcol.
extension TheirPcol {
func extraFunc() { /* do magic */ }
}
Here, you are giving all objects that conform to TheirPcol the method extraFunc() and giving it a default implementation. This accomplishes the task of extending functionality for the objects conforming to TheirPcol, and if you want it to apply to your own objects as well then you could conform your objects to TheirPcol. In many situations, however, you want to keep MyPcol as your primary protocol and just treat TheirPcol as conforming to MyPcol. Unfortunately, Swift does not currently support protocol extensions declaring conformance to other protocols.
Using TheirPcol objects as if they were MyPcol
In the use case (most likely your use case) where you really do need the separate existence of MyPcol, then as far as I am aware there is no clean way to do what you want yet. Here's a few working but non-ideal solutions:
Wrapper around TheirPcol
One potentially messy approach would be to have a struct or class like the following:
struct TheirPcolWrapper<T: TheirPcol>: MyPcol {
var object: T
func extraFunc() { /* Do magic using object */ }
}
You could theoretically use this struct as an alternative to casting, as in your example, when you need to make an existing object instance conform to MyPcol. Or, if you have functions that accept MyPcol as a generic parameter, you could create equivalent functions that take in TheirPcol, then convert it to TheirPcolWrapper and send it off to the other function taking in MyPcol.
Another thing to note is if you are being passed an object of TheirPcol, then you won't be able to create a TheirPcolWrapper instance without first casting it down to an explicit type. This is due to some generics limitations of Swift. So, an object like this could be an alternative:
struct TheirPcolWrapper: MyPcol {
var object: MyPcol
func extraFunc() { /* Do magic using object */ }
}
This would mean you could create a TheirPcolWrapper instance without knowing the explicit type of the TheirPcol you are given.
For a large project, though, both of these could get messy really fast.
Extending individual objects using a child protocol
Yet another non-ideal solution is to extend each object that you know conforms to TheirPcol and that you know you wish to support. For example, suppose you know that ObjectA and ObjectB conform to TheirPcol. You could create a child protocol of MyPcol and then explicitly declare conformance for both objects, as below:
protocol BridgedToMyPcol: TheirPcol, MyPcol {}
extension BridgedToMyPcol {
func extraFunc() {
// Do magic here, given that the object is guaranteed to conform to TheirPcol
}
}
extension ObjectA: BridgedToMyPcol {}
extension ObjectB: BridgedToMyPcol {}
Unfortunately, this approach breaks down if there are a large number of objects that you wish to support, or if you cannot know ahead of time what the objects will be. It also becomes a problem when you don't know the explicit type of a TheirPcol you are given, although you can use type(of:) to get a metatype.
A note about Swift 4
You should check out Conditional conformances, a proposal accepted for inclusion in Swift 4. Specifically, this proposal outlines the ability to have the following extension:
extension Array: Equatable where Element: Equatable {
static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool { ... }
}
While this is not quite what you are asking, at the bottom you'll find "Alternatives considered", which has a sub-section called "Extending protocols to conform to protocols", which is much more what you're trying to do. It provides the following example:
extension Collection: Equatable where Iterator.Element: Equatable {
static func ==(lhs: Self, rhs: Self) -> Bool {
// ...
}
}
Then states the following:
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.
While I realize you're not asking for the ability to have conditional conformances, this is the closest thing I could find regarding discussion of protocols being extended to conform to other protocols.

Setting a delegate generates a compile error

I want to use a strategy pattern to register a set of objects that implement a protocol. When I set this up, I get a compile error when trying to set the delegate that is part of the protocol.
For discussion purposes, I have slightly reworked the DiceGame from the Swift eBook's Delegation chapter. The changes of significance are:
protocol DiceGame - declares a delegate
class SnakesAndLadders implements DiceGame (and therefore the protocol and delegate)
class Games holds 3 instances of SnakesAndLadders as
1) a concrete class of SnakesAndLadders
2) a 'let' constant of protocol DiceGame
3) a 'var' variable of protocol DiceGame
We can set the delegate fine if we use the concrete class (snakesAndLadders). However, there is a compile error if we use 'let' to hold it as a protocol (diceGameAsLet) but it compiles if we hold the variable as a 'var' (diceGameAsVar).
It is easy to work around, however, the delegate itself never changes so should be held as a 'let' constant, as it is only the internal property that changes. I must not understand something (possibly subtle but significant) about protocols and how they work and should be used.
class Dice
{
func roll() -> Int
{
return 7 // always win :)
}
}
protocol DiceGame
{
// all DiceGames must work with a DiceGameDelegate
var delegate:DiceGameDelegate? {get set}
var dice: Dice {get}
func play()
}
protocol DiceGameDelegate
{
func gameDidStart( game:DiceGame )
func gameDidEnd( game:DiceGame )
}
class SnakesAndLadders:DiceGame
{
var delegate:DiceGameDelegate?
let dice = Dice()
func play()
{
delegate?.gameDidStart(self)
playGame()
delegate?.gameDidEnd(self)
}
private func playGame()
{
print("Playing the game here...")
}
}
class Games : DiceGameDelegate
{
let snakesAndLadders = SnakesAndLadders()
// hold the protocol, not the class
let diceGameAsLet:DiceGame = SnakesAndLadders()
var diceGameAsVar:DiceGame = SnakesAndLadders()
func setupDelegateAsClass()
{
// can assign the delegate if using the class
snakesAndLadders.delegate = self
}
func setupDelegateAsVar()
{
// if we use 'var' we can assign the delegate
diceGameAsVar.delegate = self
}
func setupDelegateAsLet()
{
// DOES NOT COMPILE - Why?
//
// We are not changing the dice game so want to use 'let', but it won't compile
// we are changing the delegate, which is declared as 'var' inside the protocol
diceGameAsLet.delegate = self
}
// MARK: - DiceGameDelegate
func gameDidStart( game:DiceGame )
{
print("Game Started")
}
func gameDidEnd( game:DiceGame )
{
print("Game Ended")
}
}
DiceGame is a heterogeneous protocol that you're using as a type; Swift will treat this type as a value type, and hence (just as for a structures), changing its mutable properties will mutate also the instance of the protocol type itself.
If you, however, add the : class keyword to the DiceGame protocol, Swift will treat it as a reference type, allowing you to mutate members of instances of it, without mutating the instance itself. Note that this will constraint the protocol as conformable to only by class types.
protocol DiceGame: class { ... }
With the addition of the above, the mutation of immutable diceGameAsLet:s properties will be allowed.
In this context, it's worth mentioning that the : class keyword is usually used to constrain protocols used as delegates (e.g., DiceGameDelegate in your example) as conformable to only by class types. With this additional constraint, the delegates can be used as types to which the delegate owner (e.g. some class) only hold a weak reference, useful in contexts where a strong reference to the delegate could create a retain cycle.
See e.g. the 2nd part of this answer for details.
The issue is that when you store something as a Protocol, even if it is a class, swift considers them to be a value type, instead of the reference type you are expecting them to be. Therefore, no part of it is allowed to be changed. Take a look at this reference for more information.

Swift Collections With Class Conforming to Protocol

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

Swift generics and protocols issue

I'm trying to push MVVM patterns on my application, but I've found that quite difficult using Swift and Generics. Example:
I have a Comic struct on my model layer. But I want my viewControllers to consume objects conforming ComicViewModel protocol in order to increase separation of concerns.
I'm wrapping the model structs in a class called Box defined like this:
public class Box<T> {
public let value: T
public init(_ value: T) { self.value = value }
}
This is used only to wrap the actual return value in a Result enum like the one explained here
However, the Swift compiler doesn't seem to like the cast from Comic to ComicViewModel even if I'm clearly defining that
func sampleComics() -> Box<Comic> {...}
protocol ComicViewModel {...}
extension Comic : ComicViewModel {...}
func fetchComicsViewModel() -> Box<ComicViewModel> {
return sampleComics() //ERROR
}
Full playground available here.
Thanks a lot for your help!
Sad to say, Swift doesn't perform implicit casting like that. As of now, you have to re-Box() the value.
func fetchComicsViewModel() -> Box<ComicViewModel> {
return Box(sampleComics().value)
}