Skip to content

Chapter 1

Informations presented on this page may be incomplete or outdated. You can find the most up-to-date version reading my book Learn Swift by examples. Beginner level

What we will do:
In this part we will learn how to create a class with properties and methods. We add a class to represent game board and another one to be an entry point to the whole game logic.

We will learn new things:

  • array,
  • class,
  • constant and variable,
  • enumeration,
  • functions,
  • range operators.

In this part we cover the following topics

Source code for this chapter.


Creating project

To play with Swift we have to use right tool. In this case I recommend Xcode which can be found by icon

Just after start a welcome screen should be displayed

Select Create a new Xcode project
If this window is not present on a screen press Shift + Command + N or select from top menu File / New / Project...

New project template selection window is then shown. Select Command line tool

Next some project identification data should be provided.

  • Product Name -- name of our "product". Choose it carefuly, because changing it in a future, although possible, is not so easy.
  • Team -- select team if any.
  • Organzation Name -- just a name of an "organization"; may be a company name, developer name as in my case or any other name.
  • Organization Identifier -- identifier of an "organization"
  • Bundle Identifier -- this is something which uniquely identify our "product" in a scale of a whole glob, and is formed as a result of concatenating Organzation Identifier and Product Name. This is why Organzation Identifier should be something realy unique -- reverse domain name is on of a method to meet this condition. As long as we only test some code or we don't want to put it in the App Store both names may be any strings we want.
  • Language -- select Swift.

After pressing the Next button folder to save project should be given


If we want to keep the code under source control, check Create Git repository on my Mac.
This was the last step and main Xcode window should appaear

Selecting main.swift file from the tree displayed in the Project navigator located on the left part of the main window displays the code located in this file

Press Command + R, select from top menu Product / Run or press "play" button to compile and execte a code

Sometimes firs run takes more time than you expect. Please be patient and be sure that console window is displeyed. If not, it can be turn on with a button located in the right-bottom corner of a main window.

That's all. Now the project is ready to be developed.



Class

Let's start with creating a Board class to be used to represent game board in our game. Press Command + N or select from top menu File / New / File...

Alternatively we can select EngineGameBattleship group in the Project navigator, right click on it and select from pop-up menu New File...

Then select macOS and Swift File and accept it pressing Next button

Provide file name, which in case of Swift doesn't have to be consistent with the class name we want to put inside (although it's a good habit)

Press Create to create the file

Initial contents of this file is almost empty except import declaration

It lets us access symbols that are declared in another module/library/framework (different names are used). The Foundation is one of the fundametal framework. It provides a base layer of functionality for apps and other frameworks, including data storage and persistence, text processing, date and time calculations, sorting and filtering, and networking.

Add

code to this file -- this would be a "frame" enclosing code of a class.

At the very first beginning add some variables to store information about board: number of rows end columns

This two simple additons requires a lot of comments.



Properties

Declaring "variables" in Swift we have two options.

  • With kyword var we can declare real variable -- a components of our class whose contents we can change through a time. It may be initiated as integer storing value 7 and later it can be changed to 3.
  • With kyword let we can declare constant (variable) -- a components of our class whose contents, when initiated, may not be change through a time. If initiated as integer storing value 7 it can not be changed and keeps this value forever.

Remarks:

  • Both types of objects sometimes are called field or property of a class.
  • Despite we differ variables and constants, if this not lead to confusion, we will refere both them as variables.

Every object in Swift must have a type and this type must be known during compilation. Type defines kind of object allowed to be stored inside an object.

  • We can explicitely define it as we did it above. From the above code we know that both rows and cols are of integer type (Int) while board is a two-dimensional array (because of double square brackets pairs [[]]) of objects of type CellType.
  • Type can be inferred based on the object substitution instruction. From the code below,

    we infer that foo variable must be an integer. So, to avoid Type annotation missing in pattern error we have to provide either a type annotation when we declare constant or variable or we have to assign a value to allow compiler infer the type of constant or variable.

Every object must have a value or must be explicitely defined as optional which is a way to indicate that this object may not have a value. At this moment we will see Class 'Board' has no initializers message. Even if we add an initializer but don't initialize all properties we will see another message: Return from initializer without initializing all stored properties. Optionals are used in situations where a value may be absent. For example an optional integer is written as Int?.

We set an optional variable to a valueless state by assigning it the special value nil. Notice that Swift's nil is not the same as nil in Objective-C or other programming languages. In Objective-C nil is a pointer to a nonexistent object. In Swift, nil is not a pointer. It is the absence of a value of a certain type. We may say, that nil is a value which tell us, that there is no correct value. In some sense it plays the same role as NaN "number" for numeric values. Optionals of any type we want can be set to nil, not just object types. Conversely, nil cannot be used with nonoptionals. If a variable (rather constant) in our code needs to take no value, we have to declare it as an optional value of the appropriate type. More details about optionals will be given in next chapter.

Once we've declared constant or variable of a certain type, we can't redeclare it, or change it to store values of different type. Nor can we change a constant into a variable or a variable into constant.

private is an access modiffier and prevents other classes from using internal details of our class. There are many reasons we do this and this is one of fundamental assumption of object oriented programming. Discussing this topic is out of scope of this book, so please refer to some external sources.



Enumerations

Our board variable is a two dimensional array of type CellType but this type has not been defined so far. To fix this we can use enumeration. An enumeration is a way we define a one common type for a group of related values. It enables us to work with those values in a type-safe way within our code. Instead of using unrelated (from compilator point of view) values we create a "set" of values and specify that in some places of our code only values from this "set" will be used.

In our case we have "set" CellType with values "hidden" under the names none, empty, hit, etc. Saying the truth we pay greater attention to names than values. Vaues are what we really need and what in most cases we will use in our code when we work with enumerations.

The meaning of each case is as follows:

  • none -- nothing, none cell should have this value; use to signal unexpected problems,
  • empty -- empty cell; cell where we can put a ship or we can shot,
  • hit -- cell where we have already shot and hit,
  • notAllowed -- cell we can't put a ship or shot,
  • rescue -- cell around sunken ship,
  • ship -- ship,
  • shot -- cell where we have already shot but miss.



Arrays

An array stores values of the same type in an indexed order. The same value can appear in an array multiple times at different positions.


["Zero", "One", "Two"]
["zero", "one", "two"]

In Swift, differently than in Objective-C, we can't create an array that has pre-allocated memory but does not contain elements. We can't create an empty array of fixed, predefined size. A good news is that we can easily concatenate arrays.


["", "", "", "", ""]
["", "", "", "THREE", ""]
["zero", "one", "two", "", "", "", "THREE", ""]
["zero", "one", "two", "", "", "", "THREE", "", "FIVE", "SIX"]

We can use subscript syntax to change a range of values at once, even it the replacement set of values has a different length than the range we are replacing.


["zero", "ONE", "TWO", "THREE", "", "", "", "THREE", "", "FIVE", "SIX"]
["zero", "ONE", "TWO", "THREE", "***", "THREE", "", "FIVE", "SIX"]
["zero", "ONE", "TWO", "THREE", "THREE", "", "FIVE", "SIX"]
value: zero
value: ONE
value: TWO
value: THREE
value: THREE
value:
value: FIVE
value: SIX
at index: 0 value: zero
at index: 1 value: ONE
at index: 2 value: TWO
at index: 3 value: THREE
at index: 4 value: THREE
at index: 5 value:
at index: 6 value: FIVE
at index: 7 value: SIX

In Swift, as in many other programming languages, two-dimensional array of objects is a one-dimensional array of one-dimensional-arrays of objects. And n-dimensional array of objects is a one-dimensional array of one-dimensional-arrays of... etc. To creating a multi-dimensional array we have to add another set of brackets. For example, to turn [Int] array into an array of arrays, we would just write [[Int]].


[[11, 12, 13, 14], [21, 22, 23], [31, 32]]
[[11, 12, 13, 14], [21, 22, 23, 24], [31, 32]]
[[11, 12, 13, 14], [21, 0, 23, 24], [31, 32]]


[[0, 0], [0, 0], [0, 0]]
[[0, 0], [0, 1], [0, 0]]


[[1, 2, 3], [4, 5, 6]]

We have to be very careful working with arrays in Swift -- generally, substitution with = operator makes a copy of an array.

For simple types such as integers and other structureswe have

If the elements in an array are instances of a class, the semantics are the same, though they might appear different at first.

More we can read in Array (Modifying Copies of Arrays).



Functions and methods

Let's make all the variables to have some values -- add the following code to our class' body

Again just a piece of code but lots to explain. init is en example of a special kind of a code called function which, in this case, is used to initialize an object. Here is a place to make all the setup needed or required to make the object usable. This special function is named in Swift as initializator while in other programming languages is refered very often as a constructor. Generaly speaking, function (sometimes subprogram or procedure in other programming languages) is a separate part of the code responsible for performing some operations and possible to be used during program execution. Subprograms are used to simplify the (structure of a) main program and increase the readability of the code.

Whether it is an initializator or "normal" funtion, it has some common properties. In short, Swift functions can be characterized as follow (this list comprises only information needed now -- full function characterustic is much more complex):

  • Each function parameter has both an argument label and parameter name.
  • We write an argument label before the parameter name, separated by a space.
  • The argument label is used when calling the function.
  • The parameter name is used in the implementation of the function.
  • By default, parameters use their parameter name as their argument label.
  • All parameters must have use unique names.
  • It is possible for multiple parameters to have the same argument label.
  • If we don't want to use an argument label for a parameter, an underscore character must be used as label for that parameter.
  • If we use an underscore character, we cannot use a parameter name as an argument label.
  • If a parameter has an argument label, the argument must be labeled when we call the function.
  • Although arguments have their labels, we cannot change arguments order.

The following examples should clarify what was writen in the list above. The simplest form of a function, taking no arguments and returning nothing, is as follow

and we call it with

We may also explicitely define, with Void keyword preceded by "right arrow", that function returns no value

or return a String object

A function with a parameter

should be called as

Missing argument label 'parameter1:' in call

results a message Missing argument label 'parameter1:' in call. In case of more than one parameter, we write a function as

and call as

Gathering all the above together

we should see the following results


functionSimplestForm
functionSimplestFormVersion2
noParameters
Parameter value: foo
Parameter value: foo : bar

Let's continue...


No return value
maxValue: Optional(9), maxPos: Optional(4)


first:second:third:fourth:fifth
definedByUser defaultValue
definedByUser alsoDefinedByUser


3.0
before swap: 3 5
after swap: 5 3
Program ended with exit code: 0

Having knowledge how functions can be written it's time to practise it. Going back to our initializer frame let's fill it with the following code

self keyword is used to avoid name conflict. Writing self.rows we tell the compiler that we want to use row variabel which is defined as a class variable. On the right hand side of the = character local (visible only in initializer) rows variable is used -- the one defined in initializer as the first argument. Without the self, statement

would be ambiguous. In the line

we create a two dimensional array of CellType objects. Simply speaking, two dimensional array is an array of arrays. In our case this is an Array of rows+2 object beeing Arrays. And an inner Array in turn consist of cols+2 object of type CellType having value none. Notice that we may but don't have to write the full enumeration case name CellType.none because the type of board is known and Swift infers what shoud be placed in front of none.

Last line is a prepareBoard() function call which is intended to prepare our gameboard, because now it is filled with meaningless nones. To simplify our further considerations we will add around our game board a one-cell width border of type notAllowed. Other cells are filled with empty value.

Although we have just refered to prepareBoard() as function, beeing more precisely we should call it method. Methods are functions that are associated with a particular type. We differ two types of methods: Instance methods and Class methods. Both types are explained in the next chapter.

Another detail worth to stop for a while is a for loop. Its syntax and meaning should be intuitively understandable. In the example above, i (as well as r and c) is a constant whose value is automatically set at the start of each iteration of the loop. As such, i does not have to be declared before it is used. It is implicitly declared simply by its inclusion in the loop declaration, without the need for a let declaration keyword. Thing we should clarify is a range operator .... Swift provides two range operators unknown in C-like world, as a shortcut for expressing a range of values.

  • The closed range operator (a...b) defines a range that runs from a to b, including both values. The value of a must not be greater than b.
  • The half-open range operator (a..<b) defines a range that runs from a to b, but does not include b. The value of a must not be greater than b. If the value of a is equal to b, than the resulting; range will be empty.
  • Starting from Swift 4 we can omit the upper or lower bound of a range specification to create a one-sided range


    ["four", "five"]
    ["four", "five"]
    ["one", "two", "three"]
    ["one", "two"]

Finally let's add a frame for our future (will be implemented in the next chapter) board printing method


Add EngineGameBattleship class

Now we will create the EngineGameBattleship class. This class would be an entry point to the whole game logic. Create a new class as we did in case of Board at the beginning of this chapter and name it EngineGameBattleship. Next fill it with a following code

This code should be self-explanatory in the context of what we have lerned in this chapter. I only mentione here that [object].[function] convention as in

case is used to call function (printBoard() in our case) on specific object (boardPlayer in our case).


Last step

In the last step we should add to the main file two lines

With the first we create an object representing our battleship game engine. The second is used to call printBoards() method from EngineGameBattleship class. Now we can run our code. As a result, we should see

Hmmm... As for now not very sepectacular but it is going to be changed in next chapters.