How can several related struct types share an init(...) method without defining read-only properties with var? - swift

I find myself very often forced to use var properties in Swift even when properties will only be assigned once.
Here's an example: The only way I have found to have several types share an init(...) is to put the init(...) in a protocol extension. However if I do this, the properties of the struct must be assigned a dummy value before the body of the init(...) in the protocol extension is run, when it will get its "real" value.
The example below runs without error. How can color be a let property but still be assigned in Piece's init(...)?
protocol Piece {
var color: Character {set get} // "w" or "b"
init()
}
extension Piece {
init(color c: Character) {
self.init()
color = c
// Some complex logic here all Pieces share
}
}
struct Knight: Piece {
var color: Character = "_" // annoying dummy value
internal init(){}
}
// ... the other Piece types here ...
let knight = Knight(color: "w")
To make this clearer, hopefully, this is what I would like instead: (This does not compile, because of let color in struct Knight.)
protocol Piece {
let color: Character {get} // "w" or "b"
}
extension Piece {
init(color c: Character) {
color = c
// Some complex logic here all Pieces share
}
}
struct Knight: Piece {
let color: Character
}
// ... the other Piece types here ...
let knight = Knight(color: "w")
Edit (after I have found an answer, see below): Another way to state the the subject line question: How can several struct types share initialization logic, while allowing read-only properties to be let?
2nd Edit Made clear that second code example doesn't compile.
3rd Edit Made clear which let color.

With some refactoring, you can achieve minimum code duplication. Here is my solution thinking about your scenario with a different perspective:
First, I noticed that you are taking only "w" or "b" as your color property value. Since you have only two (or let's say minimal) variations of inputs you can make color part of type definition itself by using protocol with associated types and generics, rather than having it as a property. By doing this you don't have to worry about setting property during initialization.
You can create a protocol i.e. PieceColor and create a new type for each color, i.e. Black, White and your Piece protocol can have an associated type confirming to PieceColor:
protocol PieceColor {
// color related common properties
// and methods
}
enum Black: PieceColor { // or can be struct
// color specific implementations
}
enum White: PieceColor { // or can be struct
// color specific implementations
}
protocol Piece {
associatedtype Color: PieceColor
}
This approach also provides safety guarantees as you are now restricting user input to only values your code is designed to handle, instead of adding additional validation to user inputs. Also, this helps you implementing specific relations between pieces depending on their color group, i.e. only opposite colors can kill each other etc.
Now for the main part of your question, instead of trying to customize initializer you can create a static method that initializes your piece and does some shared complex handling on it:
protocol Piece {
associatedtype Color: PieceColor
init()
static func customInit() -> Self
}
extension Piece {
static func customInit() -> Self {
let piece = Self()
// Some complex logic here all Pieces share
return piece
}
}
struct Knight<Color: PieceColor>: Piece {
// piece specific implementation
}
let wKnight = Knight<White>.customInit()
let bKnight = Knight<Black>.customInit()

Get-only protocol properties are cool because conforming types have a lot of flexibility in how they define the corresponding properties. All that's required is that the property is gettable.
So if you define your protocol with a get-only property:
protocol Piece {
var color: Character { get }
}
The conforming types can define the color property as a stored variable, a let constant, or a computed property.
Stored variable:
struct Queen: Piece {
var color: Character
}
Computed property:
struct King: Piece {
var color: Character { return "K" }
}
Let constant:
struct Knight: Piece {
let color: Character
}
Each of the above satisfies the gettable property requirement imposed by the protocol.
Regarding initialization. Recall that swift automatically creates default initializers for structs, and those initializers have parameters that correspond to each of the struct's stored properties. Swift may create an initializer for Queen and Knight that looks like this:
init(color: Character) {
self.color = color
}
So if you want color to be a let constant and you want to be able to configure it with an initializer, the above definitions for Piece and Knight are sufficient. You don't need to do any additional work.
You can instantiate a Knight like so:
let knight = Knight(color: "w")

After discovering Charles Srstka's answer to How to use protocols for stucts to emulate classes inheritance. I have built a solution. It isn't the prettiest but it does allow several struct types to share initialization logic while allowing properties that should be read-only, to be defined with let.
This works:
typealias PropertyValues = (Character) // could be more than one
protocol Piece {
var color: Character {get} // "w" or "b"
}
extension Piece {
static func prePropSetup(color c: Character) -> PropertyValues {
// logic all Pieces share, that needs to run
// BEFORE property assignment, goes here
return (c)
}
func postPropSetup(){
// logic all Pieces share, that needs to run
// AFTER property assignment, goes here
print("Example of property read access: \(color)")
}
}
struct Knight: Piece {
let color: Character
init(color c: Character){
(color) = Self.prePropSetup(color: c)
postPropSetup()
}
}
struct Bishop: Piece {
let color: Character
init(color c: Character){
(color) = Self.prePropSetup(color: c)
postPropSetup()
}
}
// ... the other Piece types continue here ...
let knight = Knight(color: "w")
let bishop = Bishop(color: "b")

Out of interest a completely alternative approach:
First we define a curry function - this one taken from https://github.com/pointfreeco/swift-prelude:
public func curry<A, B, C>(_ function: #escaping (A, B) -> C)
-> (A)
-> (B)
-> C {
return { (a: A) -> (B) -> C in
{ (b: B) -> C in
function(a, b)
}
}
}
Now let's suppose we have a piece that has a Role, which is the type of piece. Role can change because you can promote pawns. Here we'll use String for Role:
struct Piece {
let color: Character
var role: String
}
To share the init that we want white pieces and black pieces, we curry the init function:
let curryPieceInit = curry(Piece.init(color:role:))
and make two partially-applied init functions that bake in either w or b for the color:
let white = curryPieceInit("w")
let black = curryPieceInit("b")
And now we can finish the partial application by passing along the remaining argument to fully instantiate a chess piece:
let wBish = white("bishop")
let wQueen = white("queen")
let blackPawns = (1...8).map { black("pawn\($0)") }
Now make Role not a string but some custom type, an enum, encapsulating the logic representing the differences between the various chess pieces.
Make the Piece.init(color:role:) private, expose the curried versions.
No protocols needed.

Related

When to use type erasure in Swift?

There are already a lot of questions on how to do type erasure in Swift, and I've seen type erasure often described as an important pattern for working with protocols with associated types and generic types.
However, it seems to me like needing type erasure is often symptomatic of design problems — you're inherently "throwing away" type information (i.e. to put a value in a container or to pass it to a function), which often ultimately needs to be recovered later on anyway through verbose and brittle downcasting. Perhaps what I don't understand is the use case for a "type" like AnyHashable—PATs/protocols with self can only be used as generic constraints because they aren't reified types, which makes me wonder what compelling reasons there are to want to reify them.
In short, when is it a good idea to use type erasure in Swift? I'm looking for some general guidelines on when to use this pattern, and possibly examples of some practical use cases where type erasure is preferable to its alternatives.
I've tried to find a minimalistic example of type erasure. From my experience, it often gets more complex, and I try to avoid it as much as possible. But sometimes that's the way.
It is finally the same complexity as before, with old-school language. Excepted that old-school language was hurting you by crashing, while swift hurts you at build time.
It is meant to be a strongly typed language, so it does not fit well with generics.
Suppose you need to manage some shapes in a document.
These shapes are Identifiables, meaning they have an id which type is determined by an associated type. Int in this case.
The code below won't build because it can't use the Shape protocol directly since the type of id is an associated type that is defined by object conforming to the Shape protocol
import Foundation
protocol Shape: AnyShape, Identifiable {
var name: String { get }
}
struct Square: Shape {
var id: Int = 0
var name: String { "Square" }
}
func selectShape(_ shape: Shape) {
print("\(shape.name) selected")
}
By adding a type erased shape, you can then pass it to functions.
Thus, this will build:
import Foundation
protocol AnyShape {
var name: String { get }
}
protocol Shape: AnyShape, Identifiable {
}
struct Square: Shape {
var id: Int = 0
var name: String { "Square" }
}
func selectShape(_ shape: AnyShape) {
print("\(shape.name) selected")
}
Simple use case.
Suppose now our app connects to two shapes manufacturers servers to fetch their catalog and sync with our's.
We know shapes are shapes, all around the world, but the ACME Shape Factory index in database is an Int, while the Shapers Club use UUID..
That's at this point we need to 'recover' the type, as you say.
It is exactly what's explained when looking in the AnyHashable source doc.
Cast can't be avoided, and it is finally a good thing for the security of the app and the solidity of the models.
The first part is the protocols, and it may be verbose and become complex as the number of situations grows, but it will be in the communication foundation framework of the app, and should not change very often.
import Foundation
// MARK: - Protocols
protocol AnyShape {
var baseID: Any { get }
var name: String { get }
}
// Common functions to all shapes
extension AnyShape {
func sameObject(as shape: AnyShape) -> Bool {
switch shape.baseID.self {
case is Int:
guard let l = baseID as? UUID , let r = shape.baseID as? UUID else { return false }
return l == r
case is UUID:
guard let l = baseID as? UUID , let r = shape.baseID as? UUID else { return false }
return l == r
default:
return false
}
}
func sameShape(as shape: AnyShape) -> Bool {
return name == shape.name
}
func selectShape(_ shape: AnyShape) {
print("\(shape.name) selected")
}
}
protocol Shape: AnyShape, Identifiable {
}
extension Shape {
var baseID: Any { id }
}
The second part is the models - this will hopefully evolve as we work with more shape manufacturers.
The sensitive operation that can be done on shapes are not in this code. So no problem to create and tweak models and apis.
// MARK: - Models
struct ACME_ShapeFactory_Model {
struct Square: Shape {
var id: Int = 0
var name: String { "Square" }
var ACME_Special_Feature: Bool
}
}
struct ShapersClub_Model {
struct Square: Shape {
var id: UUID = UUID()
var name: String { "Square" }
var promoCode: String
}
}
Test
let shape1: AnyShape = ACME_ShapeFactory_Model.Square()
let shape2: AnyShape = ShapersClub_Model.Square()
let shape3: AnyShape = ShapersClub_Model.Square()
Compare two different shapes references from different manufacturers
shape1.sameObject(as: shape2) : false
-> Logic, it can't be the same item if it comes from different manufacturers
Compare two different shapes references from same manufacturers
shape2.sameObject(as: shape3) : false
-> This is a new shape, sync in catalog
Compare two identical shapes references from same manufacturers
shape2.sameObject(as: shape2) : true
-> We already have this one in the catalog
Compare two shapes from different manufacturers
shape1.sameShape(as: shape2) : true
-> Dear customer, we have two kind of squares from two manufacturers
That's all. I hope this may be of any help.
Any correction or remark is welcome.
Last word, I am quite proud of the name of my Shapes manufactures :)

Swift Protocol extension: cannot assign to property: '' is a get-only property

I think I don't understand how protocol extensions are supposed to work.
I tried this:
protocol P {
var h: [String: Any] { set get }
}
extension P {
var h: [String: Any] {
get {
return [:]
}
set {}
}
}
struct S: P {
init() {
self.h = ["o": "o"]
}
}
My goal is that S has the properties of P and doesn't need to re-declare it in the struct definition.
However, when I create let s = S(), s.h always equals [:] and not ["o":"o"].
Of course, this is because my setter is empty, but I don't know how to do what I want to achieve here.
Thanks for your help.
My goal is that S has the properties of P and doesn't need to re-declare it in the struct definition.
That's not possible in the way you're trying to do this. Protocols require certain behaviors. Conforming types must provide those behaviors. If you require storage to implement the behavior, than the conforming type has to provide the storage.
It's fine if you don't want storage, then the extension can just return values like yours does. But you can't return computed values and also have storage. What you're trying to do isn't possible. You're thinking of classes, not protocols. (If you want classes, that's fine. Use classes. There's nothing wrong with them.)
A good way to think about why this isn't possible in Swift is to consider the following case. Assume your P protocol exists in some form that achieves what you want. Now in one module we define:
struct S {}
This struct requires no storage. Any calls to S() in that module allocate nothing. (Alternately, give S some properties, and it'll allocate some specific amount of memory.)
Now in some other module, extend S to conform to P:
extension S: P {}
Where would the storage for h go? Instances may already exist at the point that this extension is even loaded. What if there were multiple protocols that wanted additional storage and all were attached to S. What offsets into the S struct should those properties be? The order would be very important to compiled code. This isn't impossible to solve (ObjC does it using a technique called associated objects), but it's a large addition to the language, and prevents a number of important optimizations, and Swift doesn't do things that way.
You were close, you just have to restate the h variable in the S Struct
protocol P {
var h: [String: Any] { set get }
}
extension P {
var h: [String: Any] {
get {
return [:]
}
set {}
}
}
struct S: P {
var h: [String: Any]
init() {
self.h = ["o": "o"]
}
}
let s = S()
print(s.h) // ["o": "o"]

Constraining one generic with another in Swift

I've run into a problem where I have some protocol:
protocol Baz {
func bar<T>(input:T)
}
The function bar is made generic because I don't want the protocol itself to have a Self(it needs to be useable in a collection). I have an implementation of the protocol defined as:
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
value = input
}
}
This gives an error because the compiler doesn't know that S and T are the same type. Ideally I should be able to write something like:
func bar<T where T==S>(input:T) {
value = input
}
or
func bar<T:S>(input:T) {
value = input
}
The first form gives a "Same-type requirement makes generic parameter 'S' and 'T' equivalent" error (which is exactly what I'm trying to do, so not sure why it's an error). The second form gives me a "Inheritance from non-protocol, non-class type 'S'".
Any ideas of on either how to get this to work, or a better design pattern in Swift?
Update: As #luk2302 pointed out, I forgot to make Foo adhere to the Baz protocol
#luk2302 has hinted at much of this in the comments, but just to make it explicit for future searchers.
protocol Baz {
func bar<T>(input:T)
}
This protocol is almost certainly useless as written. It is effectively identical to the following protocol (which is also almost completely useless):
protocol Baz {
func bar(input:Any)
}
You very likely mean (and hint that you mean):
protocol Baz {
typealias T
func bar(input: T)
}
As you note, this makes the protocol a PAT (protocol with associated type), which means you cannot put it directly into a collection. As you note, the usual solution to that, if you really need a collection of them, is a type eraser. It would be nice if Swift would automatically write the eraser for you, which it likely will be able to do in the future, and would eliminate the problem. That said, though slightly tedious, writing type erasers is very straightforward.
Now while you cannot put a PAT directly into a collection, you can put a generically-constrained PAT into a collection. So as long as you wrap the collection into a type that constrains T, it's still no problem.
If these become complex, the constraint code can become tedious and very repetitive. This can be improved through a number of techniques, however.
Generic structs with static methods can be used to avoid repeatedly providing constraints on free-functions.
The protocol can be converted into a generic struct (this formalizes the type eraser as the primary type rather than "as needed").
Protocols can be replaced with functions in many cases. For example, given this:
protocol Bar {
typealias T
func bar(input: T)
}
struct Foo : Bar {
func bar(input: Int) {}
}
You can't do this:
let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements
But you can easily do this, which is just as good:
let bars = [(Int) -> Void] = [Foo().bar]
This is particularly powerful for single-method protocols.
A mix of protocols, generics, and functions is much more powerful than trying to force everything into the protocol box, at least until protocols add a few more missing features to fulfill their promise.
(It would be easier to give specific advice to a specific problem. There is no one answer that solves all issues.)
EDITED (Workaround for "... an error because the compiler doesn't know that S and T are the same type.")
First of all: This is just an separate note (and perhaps an attempt at redemption for my previous answer that ended up being myself chasing my own tail to compute lots and lots of redundant code) in addition to Robs excellent answer.
The following workaround will possibly let your implementation protocol Foo ... / class Bas : Foo ... mimic the behaviour you initially asked for, in the sense that class method bar(...) will know if generics S and T are actually the same type, while Foo still conforms to the protocol also in the case where S is not of the same type as T.
protocol Baz {
func bar<T>(input:T)
}
class Foo<S>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
print(a.value) // 1.0
a.bar(2.0)
print(a.value) // 2.0
let myInt = 1
a.bar(myInt) // Incompatible types. Throw...
print(a.value) // 2.0
// perhaps not a loophole we indended
let myAny : Any = 3.0
a.bar(myAny)
print(a.value) // 3.0
The Any and AnyObject loophole here could be redeemed by creating a dummy type constraint that you extend all types (that you wish for to use the generics) to, however not extending Any and AnyObject.
protocol NotAnyType {}
extension Int : NotAnyType {}
extension Double : NotAnyType {}
extension Optional : NotAnyType {}
// ...
protocol Baz {
func bar<T: NotAnyType>(input:T)
}
class Foo<S: NotAnyType>: Baz {
var value:S
init(value:S) {
self.value = value
}
func bar<T: NotAnyType>(input:T) {
if input is S {
value = input as! S
}
else {
print("Incompatible types. Throw...")
}
}
}
// ok
var a = Foo(value: 1.0) // Foo<Double>
// ...
// no longer a loophole
let myAny : Any = 3.0
a.bar(myAny) // compile time error
let myAnyObject : AnyObject = 3.0
a.bar(myAnyObject) // compile time error
This, however, excludes Any and AnyObject from the generic in full (not only for "loophole casting"), which is perhaps not a sought after behaviour.

Generic Types Collection

Building on previous question which got resolved, but it led to another problem. If protocol/class types are stored in a collection, retrieving and instantiating them back throws an error. a hypothetical example is below. The paradigm is based on "Program to Interface not an implementation" What does it mean to "program to an interface"?
instantiate from protocol.Type reference dynamically at runtime
public protocol ISpeakable {
init()
func speak()
}
class Cat : ISpeakable {
required init() {}
func speak() {
println("Meow");
}
}
class Dog : ISpeakable {
required init() {}
func speak() {
println("Woof");
}
}
//Test class is not aware of the specific implementations of ISpeakable at compile time
class Test {
func instantiateAndCallSpeak<T: ISpeakable>(Animal:T.Type) {
let animal = Animal()
animal.speak()
}
}
// Users of the Test class are aware of the specific implementations at compile/runtime
//works
let t = Test()
t.instantiateAndCallSpeak(Cat.self)
t.instantiateAndCallSpeak(Dog.self)
//doesn't work if types are retrieved from a collection
//Uncomment to show Error - IAnimal.Type is not convertible to T.Type
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for animal in animals {
//t.instantiateAndCallSpeak(animal) //throws error
}
for (index:Int, value:ISpeakable.Type) in enumerate(animals) {
//t.instantiateAndCallSpeak(value) //throws error
}
Edit - My current workaround to iterate through collection but of course it's limiting as the api has to know all sorts of implementations. The other limitation is subclasses of these types (for instance PersianCat, GermanShepherd) will not have their overridden functions called or I go to Objective-C for rescue (NSClassFromString etc.) or wait for SWIFT to support this feature.
Note (background): these types are pushed into array by users of the utility and for loop is executed on notification
var animals: [ISpeakable.Type] = [Cat.self, Dog.self, Cat.self]
for Animal in animals {
if Animal is Cat.Type {
if let AnimalClass = Animal as? Cat.Type {
var instance = AnimalClass()
instance.speak()
}
} else if Animal is Dog.Type {
if let AnimalClass = Animal as? Dog.Type {
var instance = AnimalClass()
instance.speak()
}
}
}
Basically the answer is: correct, you can't do that. Swift needs to determine the concrete types of type parameters at compile time, not at runtime. This comes up in a lot of little corner cases. For instance, you can't construct a generic closure and store it in a variable without type-specifying it.
This can be a little clearer if we boil it down to a minimal test case
protocol Creatable { init() }
struct Object : Creatable { init() {} }
func instantiate<T: Creatable>(Thing: T.Type) -> T {
return Thing()
}
// works. object is of type "Object"
let object = instantiate(Object.self) // (1)
// 'Creatable.Type' is not convertible to 'T.Type'
let type: Creatable.Type = Object.self
let thing = instantiate(type) // (2)
At line 1, the compiler has a question: what type should T be in this instance of instantiate? And that's easy, it should be Object. That's a concrete type, so everything is fine.
At line 2, there's no concrete type that Swift can make T. All it has is Creatable, which is an abstract type (we know by code inspection the actual value of type, but Swift doesn't consider the value, just the type). It's ok to take and return protocols, but it's not ok to make them into type parameters. It's just not legal Swift today.
This is hinted at in the Swift Programming Language: Generic Parameters and Arguments:
When you declare a generic type, function, or initializer, you specify the type parameters that the generic type, function, or initializer can work with. These type parameters act as placeholders that are replaced by actual concrete type arguments when an instance of a generic type is created or a generic function or initializer is called. (emphasis mine)
You'll need to do whatever you're trying to do another way in Swift.
As a fun bonus, try explicitly asking for the impossible:
let thing = instantiate(Creatable.self)
And... swift crashes.
From your further comments, I think closures do exactly what you're looking for. You've made your protocol require trivial construction (init()), but that's an unnecessary restriction. You just need the caller to tell the function how to construct the object. That's easy with a closure, and there is no need for type parameterization at all this way. This isn't a work-around; I believe this is the better way to implement that pattern you're describing. Consider the following (some minor changes to make the example more Swift-like):
// Removed init(). There's no need for it to be trivially creatable.
// Cocoa protocols that indicate a method generally end in "ing"
// (NSCopying, NSCoding, NSLocking). They do not include "I"
public protocol Speaking {
func speak()
}
// Converted these to structs since that's all that's required for
// this example, but it works as well for classes.
struct Cat : Speaking {
func speak() {
println("Meow");
}
}
struct Dog : Speaking {
func speak() {
println("Woof");
}
}
// Demonstrating a more complex object that is easy with closures,
// but hard with your original protocol
struct Person: Speaking {
let name: String
func speak() {
println("My name is \(name)")
}
}
// Removed Test class. There was no need for it in the example,
// but it works fine if you add it.
// You pass a closure that returns a Speaking. We don't care *how* it does
// that. It doesn't have to be by construction. It could return an existing one.
func instantiateAndCallSpeak(builder: () -> Speaking) {
let animal = builder()
animal.speak()
}
// Can call with an immediate form.
// Note that Cat and Dog are not created here. They are not created until builder()
// is called above. #autoclosure would avoid the braces, but I typically avoid it.
instantiateAndCallSpeak { Cat() }
instantiateAndCallSpeak { Dog() }
// Can put them in an array, though we do have to specify the type here. You could
// create a "typealias SpeakingBuilder = () -> Speaking" if that came up a lot.
// Again note that no Speaking objects are created here. These are closures that
// will generate objects when applied.
// Notice how easy it is to pass parameters here? These don't all have to have the
// same initializers.
let animalBuilders: [() -> Speaking] = [{ Cat() } , { Dog() }, { Person(name: "Rob") }]
for animal in animalBuilders {
instantiateAndCallSpeak(animal)
}

Page 37 of The Swift Programming Language: func works in code but not as method of a struct

I'm trying an exercise from page 37 of the Swift Programming Language book from Apple Here's a struct with two enums (from the Swift Programming reference):
struct Card {
var rank: Rank
var suit: Suits
func description() -> String {
return "The \(rank.ofRank()) of \(suit.ofSuit())"
}
func createDeck() -> (Card[]){
var deck = Card []()
for rangeOfSuits in 1...4 {
for rangeOfRanks in 1...13 {
println("\(rangeOfRanks)")
deck.append(Card(rank: Rank.fromRaw(rangeOfRanks)!, suit: Suits.fromRaw(rangeOfSuits)!))
}
}
return (deck)
}
}
That second method: createDeck() doesn't work. When used
myDeck.createDeck()
gives compile error: "Card() does not have a member 'createDeck'"
However, remove it from the Struct and everything works fine:
func createDeck () -> (Card[]){
var deck = Card []()
for rangeOfSuits in 1...4 {
for rangeOfRanks in 1...13 {
deck.append(Card(rank: Rank.fromRaw(rangeOfRanks)!, suit: Suits.fromRaw(rangeOfSuits)!))
}
}
return (deck)
}
myDeck = createDeck()
myDeck.count //yields 52, as it should
myDeck[51].description() //yields King of Clubs as it should.
My answer to the question is: on page 384 of the Swift Programming Language:
“Type Methods
Instance methods, as described above, are methods that are called on an instance of a particular type. You can also define methods that are called on the type itself. These kinds of methods are called type methods. You indicate type methods for classes by writing the keyword class before the method’s func keyword, and type methods for structures and enumerations by writing the keyword static before the method’s func keyword.”
I am not clear on why one has to do this, but the method works when I add "static" in front of it and fails when I don't.
myDeck.createDeck() by itself makes no sense because you have no Card. A struct is like a class; to send an instance method to it, you need an instance. Instantiate first:
var myDeck = Card() // not really, though
myDeck.createDeck()
However, that code won't work either, because () is not a valid initializer for Card. So replace the first line by an actual initializer.
Use the static keyword to make a struct func that does not need an instance to exist.
In your example:
struct Card {
// ...
static func createDeck() -> (Card[]){
var deck = Card []()
for rangeOfSuits in 1...4 {
for rangeOfRanks in 1...13 {
println("\(rangeOfRanks)")
deck.append(Card(rank: Rank.fromRaw(rangeOfRanks)!, suit: Suits.fromRaw(rangeOfSuits)!))
}
}
return (deck)
}
}
Now you may use:
var deck = Card.createDeck()