Apple released Swift officially as an alternative language for development, apart from Objective-C. It looks and reads like an easy-to-use scripting-like language. It is a modern language, and has the features of most modern languages such as Python, Ruby, Lua and Scala. Swift removes the need for semicolons and uses the standard double slash for comments, amongst other things, as shown here:
//This is a comment
The println
function is used for printing data. Swift also has a more evolved println
function that allows for formatted printing. The value of a variable can be inserted into a string by enclosing the variable name in brackets preceded by a slash: (varname)
. This format has to be enclosed in double quotes to make it work.
println("Hello World") println("This is fine too"); var ver = 6 println("Hello from Xcode (ver)")
We can add two numbers, say 1 and 3, but if our program just added 1 and 3, we would not have much use of the same. If we wanted to write a program that adds two numbers that we pass and it returns a result, then it would be of some use. There are a couple of things that need attention here; we need to first store the numbers in some space and then be able to access them. Variables are basically storage spaces in the system that hold a value of a particular type, and access is via named labels. It would be easier to remember a variable called age
than one called 0xff22400d3
. Swift has two types of variables; mutable and immutable. Immutable variables are called constants. The values that they hold cannot be changed.
When dealing with large numbers, it is difficult to type them in or read them while trying to group them mentally. A number like 123456789 is not as easy to decipher as 123,456,789 is. While assigning the same number to variables, it is not easy to read and check the same, as you cannot set something like var largeNum = 1,234,567.89
. However, Swift allows you to format your numbers with an underscore as 1_234.50,
or in the preceding example as 1_234_567.89
:
var thousands = 4_693.45 var billions = 3_142_857_143 var crazyNumber = 1_23_456_7_890
In Swift, a variable can be of any type as available. However, there are some internal types that are provided by the system, they are as follows:
Int
, Float
, Double
String
Array
, Dictionary
Bool
All variables in Swift need to be declared prior to using them, as Swift is a typesafe language. The compiler checks for the types and highlights of potential issues with the use of non-matching variable types used. So, a variable that is declared as Int
cannot hold any other type than Int
. The two keywords used to declare variables are let
and var
; let
declares constants (the value for these variables can be set once and not changed after that) and var
declares mutable variables:
var publisher:String let name:String = "Learning Xcode 6" var year:Int = 2014
Variables can either just be declared or can be both, declared and assigned a value. Once declared, a mutable variable can be reassigned a new value; whereas, if we try to do so with a constant, the compiler would complain:
name = "Learning Visual Studio" // Compiler complains year = 2015 // Works ok
The Swift compiler is quite intelligent and it can implicitly define variables. It knows that when you type the following:
let author = "Jayant Varma"
The variable author
is of type String
as the value assigned to it is of type String
and it does not have to be explicitly declared.
Multiple variables can be declared on the same line too by separating them with commas.
var a = 1, b = 2.3, c = "Four" var x, y, x : Double
Implicit assignment works even with numbers, and all the numbers without the decimal point are considered of type Int
and all with a decimal point are of type Double
:
var nextYear = year + 1 // autocasts to type Int
In cases where you wanted this variable to be Double
and not Int
, you could explicitly assign it to type Double
as follows:
var nextYearAndHalf = Double(year) + 1.5
When working with iOS SDK, you will realize that you will need another type of variable for numbers, called CGFloat
; while it is a floating point number, it is a different structure than Double
of Float
or Int
.
As mentioned earlier, the Swift compiler expects the types to be of the expected types, so while a number is a number and broadly we consider 1, 1.0, and "1.0" as the same, for computers, and more so for Swift, these are three different types, namely Int
, Double
, and String
. When working with different types, we need to convert them to the same type in order for it to work:
var pi = 3.14 // autocast to Double var e = 3 // autocast to Int var pie = pi + e // Compiler error - not similar types
To resolve such issues, we need to cast the values to the expected type (if they can be converted), we can fix this by using the following line of code:
var pie = pi + Double(e) // works as expected
Or, as we discussed in the previous example, regarding CGFloats
, you could use the following line of code:
var cgPie = CGFloat(pie) // this is now of CGFloat type
The same is true for strings; we can easily do something like the following:
var hello = "Hello" var world = "World" var test = hello + world
However, we get an error when we try something like this:
var appname = "Xcode" var appversion = 6 var bookname = appname + appversion // we get an error
This is because we are trying to add type String
with type Int
. We can convert Int
to String
and it would work fine, we can do that easily by using the following line of code:
var bookname = appname + String(appversion)
Or, use the string formatting feature in Swift:
var sBookname = "(appname) (appversion)"
Booleans are simple true
and false
values, and the variable type in Swift is called Bool
. They form the basis for a lot of logic and conditions. It can be used either by setting them explicitly via declaration, or can be set via conditions and never assigned to variables, as shown here:
var worksOnWindows = false if appversion > 6 { println("Cutting edge version") if worksOnWindows == true { println("and it works on Windows too") } }
In the first line, we explicitly set the value of a variable to type Bool,
and in the second line, we use the result of the condition appversion > 6
, which evaluates to either true
or false
.
Sometimes, we need multiple variables, say a student enrolled at university, and we want to store the name of the classes they are enrolled in. We can have something like this:
var class1 = "Math" var class2 = "Creative English" var class3 = "Compiler Theory"
This works but is not practical as the more classes the student is enrolled in, the more variables there will be. The student might enroll in a few classes this semester and might take additional classes in the next semester. How would we know how many classes the student was enrolled in? How would we access the variable names in our code? If we were to use the classXX
format by name, we are hardcoding our logic and this would not be good. For this purpose we use an Array
.
An array stores a list of values and these can be accessed via a sequential index. In simple terms, we can access the list using a numeric index that is in sequence, starting from 0
up to the number of values held in the array. Arrays are declared in Swift as follows:
var arrClasses:[String] //shorthand declaration
The alternative way is to declare an array as follows:
var arrClasses = Array<String>() //declaration
This tells the compiler that arrClasses
is a type Array
variable that holds String
values. With arrays, we also get additional functions that allow us to work with the array, such as, to get the number of elements in the array we can use the count
function.
Arrays need to be initialized, we can either initialize them with a blank list, or we can populate the values while initializing them, as shown in the following code:
var arrClasses:[String]
This only declares the variable called arrClasses
to be of type array of string, but it is not initialized and cannot be used prior to initialization:
arrClasses = [] //Initialized as blank
A blank array can be declared and initialized by using var arrBlank = [Int]()
. It can also be initialized with a set of default repeating values like this:
var arrZeros = [Int](count:10, repeatedValue:0) // Array of Ints with 10 items and all initialized to 0
Arrays can also be initialized with default values, as follows:
var arrSlots = ["Morning", "Afternoon", "Evening", "Night"]
This would have created an array with four string elements. If we will not have the need to modify this array, we can declare it with let
instead.
In our example, we can now have the student enroll in several classes and we can know the classes the student is enrolled in and also the number by accessing the array:
var numberOfClasses = arrClasses.count
Apart from the count
property, there is another property called isEmpty
that returns true
if there are no elements in the array.
More elements can be added to the array using the append
method, as shown in the following code:
arrClasses.append("Finance") arrClasses.append("Learning MS Office")
Or, we can also use the +=
operator as follows:
arrClasses += ["Algorithms"] arrClasses += ["Basket Weaving", "Bread Making", "Cheese Tasting"] numberOfClasses = arrClasses.count
The index can be used to access the elements or to modify the elements, as follows:
arrClasses[0] = "Basics of Finance" // Modifies the 1st element
Elements can be removed using the remove
method. The remove
function returns the element value it is removing:
arrClasses.removeAtIndex(1) // Removes the 2nd element
We can also remove the last element or remove all the elements using the removeLast
or removeAll
functions.
We can also add an item specifically at an index position using the insert
function:
arrClasses.insert("Creative Writing", atIndex: 2)
Dictionaries are also called key-value pairs or associative index. This is because, instead of a numeric index value, we have an associative value to access the element or a key to retrieve the value. This is quite useful in cases where you might want to access an item (the value) by a key (usually a string) instead of using a numerical index. Declaring dictionaries is similar to declaring arrays. However, the only difference is that Swift expects that the variable types for both the key and the value be specified:
var dClassTimes : [String:String] // Specifies both the key and the value would be of type string
An example for using an associative array / dictionary could be to store the subject code with the subject name:
var myCourse : [String:String] = ["CP1001":"Computing Basics", "FP1001":"Finance Basics", "CP2002":"Algorithms"]
Then, we can simply get the subject name using the following code:
var theSubject = myCourse["CP1001"] println("The subject name for CP1001 is (theSubject!)") var _CODE_ = "CP1005" println("The subject name for CP1001 is (myCourse[_CODE_])") if let invSubject = myCourse["CPxxxx"] { println("The invalid subject is (invSubject)") }
The functions to work with dictionary variables are similar to the ones available with arrays, including the isEmpty
and the count
functions.
In a dictionary object, new values can be added by simply specifying the value for a key:
myCourse["OZ1001"] = "Custom Subject"
This would replace the value that has the key CP1001
with this new text:
myCourse["CP1001"] = "Basics of Computing"
Another alternative for replacing a value in the dictionary object is using the updateValue
function and pass the forKey
parameter as the key:
myCourse.updateValue("Basics of Finance", forKey:"FP1001")
To remove a value from the dictionary, either set the value of the key to nil
or use the function removeValueForKey
:
myCourse["CP2002"] = nil myCourse.removeValueForKey("CP1001") myCourse.removeValueForKey("LP1001") // Non existent key-value
The function returns the value being removed or nil
if none existed.
The elements in a dictionary object can be iterated using the for
loop:
for (key, value) in myCourse { println("The key :(key) has a value of : (value)") }
Each item in a dictionary object is returned as a tuple (key, value) and all of the keys, or all of the values can also be accessed as arrays:
var allKeys = myCourse.keys.array var allValues = myCourse.values.array
Tuples are a simple concept. It is a data structure that holds multiple parts. The values inside of a tuple can be of the same type or different types. Declaring a tuple is easy and can be defined as TupleName and the value parts inside brackets:
var webError = (Int, String) webError = (404, "Page not found") var myTuple = ("07:00", "Morning Run", true)
To access the members of a tuple, the values can be stored into normal variables. This reverse assignment is called decomposition:
var (errorCode:Int, errorDescription:String) = webError
Since tuples are not arrays or dictionary objects, they cannot be accessed using indices, sequences, or associates. In such a scenario, Swift creates sequential member properties that allow access to the tuple members:
println("The components of webError are code: (webError.0) and Description: (webError.1)")
It is not always very easy to use the sequential members to access tuples. How would you know what was .0
and what was .1
, this is where named members make the access easier. Creating named members just involves defining a name for the tuple members:
var namedError = (code:200, description:"OK") println("The components of named error are code : (namedError.code) and description : (namedError.description)")
These components can now be accessed using the named members, as follows:
var theCode = namedError.code // returns the error code
Working with strings is also quite easy in Swift. Like arrays, strings can be iterated using a for
loop:
var testing = "This is a test" for char in testing { println("The char is (char)") }
Any
needs to be mentioned, as it is a type that matches any of the types. This is useful to store a type that could change:
var someValue:Any = "Frogs" someValue = 6 someValue = "Green Tree Frogs"
If instead of using the Any
type in the preceding example, you used something like this:
var stringValue = "Green Frogs" stringValue = 6
Then Playgrounds would complain, as the stringValue
variable has been implicitly defined as String
. Assigning an integer value to a string variable is not acceptable. This functionality allows Xcode to be typesafe and ensures that even before you run your code, you are accidentally assigning wrong value types to the variables. The Any
variable type is similar to id
in Objective-C:
var castedString = someValue as String // the last value of someValue was "Green Tree Frogs"
The AnyObject
type is similar to the Any
variable type and the values need to be cast to a specific type to work with. If a value is of type Any
and you compare it to a String
, it would not work because type Any
is not the same as type String
and there is no function that compares these two using the ==
operator. So, it must be cast to a variable of type String
and then it could be easily compared.
Like the C variants, Swift offers a series of control flow options that allow conditional execution or multiple executions of other statements. Swift has most of the C-type constructs providing the familiar for
loops, if..else
, switch
, and a couple of newer ones.
The classic if
statements are available in Swift, and the commands that need to be executed if the condition evaluates to true
or false
is enclosed in curly brackets:
var berry = "black" if berry == "red" { println ("This is not a blackberry") }
We see that the condition does not evaluate to true
as the value in the variable called berry
is black
and we are checking it for red
. So we do not see any output.
The if
statement also has functionality to do something when the condition does not evaluate to true
. So, it basically has two sets of statements; one that evaluates when the condition is true
and the other when it does not:
var numApples = 3 if numApples > 5 { println("You have enough Apples") } else { println("You might want to buy some more Apples") }
Sometimes, there are more than a single if..else
type conditions. In cases where you need more than two conditions, we use the if..elseif..else
statement:
var platform = "MAC" if platform == "MAC" { println("This is running Apple OS X") }else if platform == "WIN" { println("This is running Microsoft Windows") }else{ println("This could be running Linux") }
We can add more else if
statements to check for more conditions.
The ternary operator ?:
is also referred to as the Elvis Operators since it looks like Elvis' hairdo in ASCII. This is the shortcut for an if...else
condition and can be used as follows:
var eggs = 0 var bread = eggs > 0 ? 12 : 1
This checks for a condition, and if it is true
, evaluates to the statement between ?
and :
, and if the condition is not satisfied, it evaluates to the statement after the :
.
When you want to perform some statements repeatedly, you can use loops. This makes it easy rather than write them several times over.
The classic for
loop is for-condition-increment
loop:
for var i=0; i<=10; i++ { println("The value of i is (i)") }
You can also have multiple assignments or increments in the preceding for
loop, as shown:
for var i=0, j=0, k=0; i<=10; i++, j--, k+=5 { println("The value of i=(i) j=(j) k=(k)") }
The new for
loop added in Swift is the for-in
loop that we have already seen in use earlier for enumerating arrays, and with strings. The for-in
loop provides a range of values for the loop to iterate through.
The following is an example of using the for
loop with strings:
for i in "Hello" { println("The value of i = (i)") }
The following is an example of using the for
loop with numeric ranges:
for i in 1...10 { println("The value of i = (i)") }
Swift has the concept of half closed and full closed ranges. The preceding example is a full closed range that prints the value of i
from 1 through to 10:
for i in 1..<10 { println("The value of i = (i)") }
This would print the values of i
from 1 to 9, not including the last value that we specified of 10.
The other alternative to for
loops is a while
loop; this performs the statements repeatedly as long as the condition provided evaluates to true
, as shown in the following code
var coins = 0 while coins < 10 { coins += 1 println("We have mined (coins) coins") }
This will run and keep incrementing our coins, and when it reaches the value of 10, it stops, as our condition is that the coins
should be less than 10, so the maximum coins
we should have are 9. However, because when the value of coins
is 9, the while
condition is evaluated as true
and run once again, incrementing our coins
count to 10.
There is a variation on the while
condition and that is the do..while
loop. The difference between the while
loop and the do..while
loop is that the while
loop may or may not execute, depending on the condition; whereas the do..while
loop will execute at least once before evaluating the condition to determine running the code or not:
coins = 0 do { coins += 1 println("We have mined (coins) coins") }while coins < 10
We find that the loop ran once again and now we have 10 coins. When the value of coins reaches 10, the loop is stopped and not executed again.
Seeing the two variations, be careful when you use them, they could give you unexpected results due to the way they work.
The if
statement works with conditions and mostly we use a series of if
statements to check for the value of a variable. switch
is a better approach that allows checking for multiple values.
var age1 = 28 switch age1 { case 0: println("Baby") case 1: println("Toddler") }
This code would cause the compiler to complain, especially since the Swift compiler attempts to reduce programming errors. We have the code that is not faulty, but we have not handled the scenario where none of the cases are matched as in the preceding example. Swift expects a default
value to handle the other scenarios that do not match. The working code would look like this:
var age1 = 28 switch age1 { case 0: println("Baby") case 1: println("Toddler") default: println("Others") }
If we were to classify the ages, it would be quite a hassle to type in every possible match, we might just want that 1 to 3 be a toddler and 4 to 12 be a child, as shown ahead:
var age1 = 28 switch age1 { case 0: println("Baby") case 1, 2, 3: println("Toddler") case 4, 5, 6, 7, 8, 9, 10, 11, 12: println("Child") default: println("Other") }
This is a bit tedious, typing in all of the values. Swift helps us out here with ranges, we can use the range operator ...
(three dots) as shown in the following code:
var age1 = 28 switch age1 { case 0: println("Baby") case 1...3: println("Toddler") case 4...12: println("Child") case 13...19: println("Teen") default: println("Other") }
We can also use non-contiguous ranges, such as 0 to 17 and 60 to 99 in this example to segregate them as working and non-working population:
var age1 = 28 switch age1 { case 0...17, 60...99: println("Not Working") default: println("Working") }
The switch
statement can handle pretty much any type of value, not just numeric. It can handle strings, tuples, and many more.
var berry1 = "red" switch berry1 { case "red" : println("This is a red berry") case "blue" : println("This is a blue berry") case "black" : println("This is a black berry") default println("This is a not a berry") }
Character ranges can also be used like "a"..."i", "l"..."x"
.
Tuples can also be used for switch case matching:
var aScore = (100, "Temple Bun") switch aScore { case (1000, "Temple Bun"): println ("You made the high score") default: println("A little more (1000-aScore.0) and you can beat the high score") }
With tuples, you can choose to ignore a value, that is, say you wanted to check the scores of a particular game or just a particular score for any game, as in the following code:
switch aScore{ case (_, "Temple Bun"): println("Wow, I could never score on this game") case ( 100, _): println("Yay! You are a player… I have not yet tried (aScore.1)") default: println("All play makes Jack a dull boy") }
There are a couple more tricks with Swift. In the previous example, since our score is 100, the first condition matches and it prints out the "Wow, I could never …
" message. This is the default behavior, and the expected one too. In some cases, you might want it to continue matching the next case. In such a scenario, you can use the fallthrough
keyword. By default, the code exits the switch block and does not check the next case, with fallthrough
, the statements for the next case are also executed, irrespective of the match, as shown here:
switch aScore{ case (_, "Temple Bun"): println("Wow, I could never score on this game") fallthrough case ( 100, _): println("Yay! You are a player… I have not yet tried (aScore.1)") default: println("All play makes Jack a dull boy") }
Now, we will see both the messages, "Wow, I could…
" and "Yay! You are a …
".
In another example, we can see this:
var theNumber = 1 switch (theNumber) { case 1: println("One") fallthrough case 2: println("Two") case 3: println("Three") default: println("Default") }
You will see that in the output it prints One
and Two
. This is useful in cases where you might want to only execute println("Two")
if theNumber
is 2
, but display both One
and Two
if it is 1.
In the examples, you will notice that we used aScore.0
or aScore.1
to access the first or the second member of the tuple. This is fine, but sometimes you want to access them with a proper variable name. We can do this via value binding, and use it as follows:
switch aScore{ case (let score, "Temple Bun"): println("Wow, I could never score (score) ") case ( 100, let game): println("Yay! You are a player… I have not yet tried (game)") default: println("All play makes Jack a dull boy") }
switch
is even more powerful in Swift and it offers more condition checking options via the where
clause:
var marks = (80, "Math") switch marks { case (let score, let subject) where score>75 && subject == "Math" : println("You got a HD in Math") case (50..<80, let subject): println("You passed the subject (subject)" case (let score, let subject): println("You scored (score) in (subject)") default: println("") }
Functions in Swift are declared using the func
keyword. A function encloses a set of statements inside a block. When a function is called, all of the statements inside that block that make up the function are executed.
Functions may or may not return any values. Although, functions in Swift can return multiple values via tuples.
The following is an example of a no-return value function:
func noValues(){ println("this returns no values") }
The following is an example of a single-return value function:
func returnOne() -> Int{ return 1 } var one = returnOne()
You might note that when a function returns a value, we need to specify the return type of the value and that the function is written with a ->
at the end followed by the return type.
The following is an example of a multiple-return value function:
func returnMany() -> (Int, String){ return (404, "Page not found") } var error = returnMany()
The multiple return values are in the form of a tuple and it is imperative to define the return values. In the previous example, we return an Int
and a String
value. While in this example, we are returning the tuple without named parameters; depending on your needs, you could return tuples with named parameters.
Functions might accept parameters that allow you to pass values that may influence the functionality of the function. We have seen examples of functions that do not require any parameters in the previous examples. If we need to pass parameters, we need to specify the type of the parameter value we shall pass to the function.
The following is an example of a single parameter function:
func strLength(theStr:String) -> Int { return countElements(theStr) } var len = strLength("Hello World")
The following is an example of a multiple parameter function:
func multiplyBy(num1:Int, num2:Int) -> Int { return num1 * num2 } var res = multiplyBy(7, 4)
You might have noticed that we pass parameters to the functions, but as a developer you might want to have named parameters that would be self-explanatory as to what the parameters are. It would not be very clear on the purpose of the parameters passed. To name the parameters, we need to give them a name. These are called external parameter names, the internal names are used by the function:
func appendString(theString first: String, withString second: String) -> String { return "(first) (second)" } var strRes = appendString("Hello", "World!")
The preceding code will give a compiler error because appendString
was declared to use named functions and it is missing the named arguments of first and second. To be able to call the appendString
function, we need to use the named parameters:
var strRes = appendString(theString:"Hello", withString: "World!")
Swift has a shorthand method that helps avoid this scenario by simply using a hash symbol (#
). This ensures that both the parameters (internal and external) have the same name. The following code explains this:
func appendStrings(#theString: String, #withString: String) -> String { return "(theString) (withString)" } var theBook = appendStrings(theString: "Swift", withString: "Rocks")
For some functions, you can also provide default values that allow you to omit passing that parameter:
func replicate(times:Int, theString:String = "-")->String{ var result = "" for i in 1...times { result += theString } return result } var dashes = replicate(20)
This calls the replicate
function and passes 20 for times
and a value of "-
" as theString
. If we want to call this function with a different string to replicate, we cannot simply call it as follows:
var plusses = replicate(20, "+")
This would give an error and to fix it, we need to call it as follows:
var plusses = replicate(20, theString:"+")
While we can have functions with no parameters, we could also have functions with single or multiple parameters; but in some cases, there might be a scenario to pass an unknown number of parameters. With a known number of parameters, we can define the number of parameters. With unknown, we would never know if there will be any parameters and if there are; the number of parameters would be unknown. These are called parameter arrays or variadic parameters. This is denoted by three dots ...
after the parameter type, as indicated in the following code:
func add(numbers:Int...)->Int{ var result = 0 for i in numbers{ result += i } return result } var total = add(3, 7, 2, 9, 12, 11) // 44
There are more advanced features associated with functions. This chapter is a quick intro to Swift and hence they are not covered here. Please refer to a Swift book for comprehensive coverage of advanced features such as in-out parameters, constant and variable parameters, function types, and using functions as return types. Apple has a free book called The Swift Programming Language that you can download and refer to, among other options.
Closures might sound like a fanciful name but in simple terms, they are what Apple introduced as Blocks for Objective-C. This is functional code that can be called at a later time. These retain their own scopes for variables and contained functions and can be executed in a different scope than they were created in. These are so called because they form an enclosure over the variables and constants, as shown in the following code:
func startFrom(startValue:Int) -> (()->Int) { var _startVal = startValue func increment() -> Int{ return ++_starVal } return increment }
In the previous example, we return a function and if you noticed the return type, it is (()-> Int)
, which specifies that the return type is a function. This function takes no parameters and returns an Int
value. First, we save the value passed into a local variable called _startVal
. Then we return the value while incrementing it using ++_startVal
as shown here:
var counter1 = startFrom(1) var counter2 = startFrom(10) counter1() // return 2 counter2() // return 11
The preceding example demonstrates that both of the counter functions contain their own set of variables and are independent of each other.
Swift has an object-oriented paradigm for you to declare and create classes, structures and enumerations. The primitives such as Int
, String
, Float
, Double
, to name a few, are structures or structs as they are now called.
To define a class, we simply use the keyword class
. A class can contain variables, functions and methods that have initializers to set up the initial values; they can be extended and they can also conform to protocols to provide standard functionality. The following code explains this:
class Stooge{ var name:String = "" func getName() -> String{ return self.name } } var stooge1 = Stooge() stooge1.name = "Moe" println("The name of the first stooge is (stooge1.getName())")
This is a bit cumbersome. What if we could initialize our object with a default name? How about using the following:
var stooge2 = Stooge("Larry")
We get a compiler error. To be able to use it this way, we need to create initializers using the init
function. Our Stooge
class would need modifications, and will now look something like the following code snippet:
class Stooge{ var name: String = "" func lengthName() -> Int { return countElements(self.name) } init(forName: String){ self.name = forName } }
However, we still get a compiler error and this time the compiler tells us that it is looking for a named parameter name. So we cannot simply call this as Stooge("Larry")
; instead, we need to call it as Stooge(forName:"Larry")
.
Swift provides us a way to ignore the external name by adding an underscore before the internal name. Then we can call this without the forName:
parameter:
init(_ forName:String){ self.name = forName }
And now, we can initialize our new stooge
variable as follows:
var stooge2 = Stooge("Larry")
We created a property called name
in the previous example, and it can be read and updated directly. Say we add another property in the year of first appearance as:
class Stooge{ var name:String var firstAppear:Int init(_ forName:String){ self.name = forName self.firstAppear = 1900 } convenience init(){ self.init("Unknown") } convenience init(_ forName:String, _ year:Int){ self.init(forName) self.firstAppear = year } func lengthName() -> Int { return countElements(self.name) } }
We have two convenience
functions for init
. The convenience
functions are defined to allow different function signatures. In our case, we can call the init
function without any parameters, with just the name or with both, the name and the year. If we did not use the convenience
keyword, the compiler would complain thinking we are redefining the function with a new signature:
var stooge3 = Stooge("Moe", 1934)
The properties in this class, name
and firstAppear
, are both directly accessible. You could also define setters and getters that could be used to set the values as required. These are useful as calculated properties:
var theYear:Int { get { return self.firstAppear - 1900 } set { self.firstAppear = newValue + 1900 } }
We could now use this like a property and it will interact and set/get the value from the firstAppear
variable:
println("(stooge3.firstAppear)") // Prints 1934 stooge3.theYear = 67 println("(stooge3.firstAppear)") // Prints 1967
Enumerations are data types that have named values and behave like constants. They too, like structures and classes, have properties and functions associated including initializer functions:
enum eStooges:Int{ case Moe = 1 case Larry case Curly case Shemp case Joe } var aStooge = eStooges.Moe
You could add a function to this like toString
, which returns the name of Stooge
as a string:
enum eStooges:Int{ case Moe = 1 case Larry case Curly case Shemp case Joe func toString()->String{ switch self{ case .Moe: return "Moe" case .Larry: return "Larry" case .Curly: return "Curly" case .Shemp: return "Shemp" case .Joe: return "Joe" } } } var aStooge = eStooges.Moe println(aStooge.toString()) // Moe
You can also use the enumerations without the enumeration name directly with the member name, like this:
var theStooge: eStooges = .Curly println("(theStooge.toString())") // Curly println("(theStooge.rawValue)") // 3
When using enumerations, it will always number them starting from 0, however, if you want to change that start index, you can define the first value and the rest will be assigned a sequential value. You can also assign values to each of the members or they are assigned the next number (that is, sequential).
This is one of the most interesting language features in Swift. It allows adding of functions and features to an existing class. We can extend an Int
variable and add an extension called "square" that returns the square of the integer:
extension Int{ var square:Int { return self * self } }
You can now type 4.square
in the Playground and the result 16
will show.
We can create another extension for strings called reverse
:
extension String { var reverse:String{ var result:String = "" for i in self { result = String(i) + result } return result } } "Hello".reverse // shows olleH
We can use most of the basic operators to perform basic operations, as you have seen in the previous examples. With Swift, we can also re-declare these operators to perform custom operations. Some of the basic operators can be overridden to provide additional functionality.
There are three positions for operators, prefix
, postfix
, and infix
. They are defined depending on the position of where the operator lies.
Prefix operators are those that have the operator before the expression, such as ++a
or !b
. Here's an example of using the square root symbol operator (√
) and making it function as expected:
prefix operator √ {} prefix func √ (theNumber:Double) -> Double { return sqrt(theNumber) } var sqRoot1 = √144 // 12.0 var sqRoot2 = √16 // 4.0
Postfix operators appear at the end of the expression, such as a++
. Here is an example of using the %
postfix operator that returns the percentage:
postfix operator %{} postfix func %(theNumber: Double) -> Double { return theNumber / 100 } var rads1 = 60% // 0.6 var rads2 = 152% // 1.52
The infix operator appears in between two expressions, such as the equality operator ==
or +=
, or >>
, <<
and the bitwise operators &&
, ||
,. Let us create a custom infix operator that replicates a string as many times as the number following it:
infix operator ** {} func ** (theString:String, times:Int) -> String{ var res = theString for i in 1..<times { res += theString } return res } var buffer = "Hello" ** 5 //HelloHelloHelloHelloHello
18.118.2.15