WHAT YOU WILL LEARN IN THIS CHAPTER:
?
characterIn Chapter 1, you took a quick look at the syntax of Swift statements, as well as how to declare variables and constants quickly using type inference. In this chapter, you will learn more about the various data types available in the language.
In addition to supporting the various basic data types available in most programming languages, Swift also introduces new data types not available in Objective-C. Such new data types include the following:
Swift is a type-safe language. In most cases, you have to perform explicit type conversions when assigning values from one type to another. Also, variables that are not assigned a value are not allowed to be used in a statement and will be flagged as errors.
Like most programming languages, Swift provides the following basic data types:
Integers are whole numbers with no fractional parts. Integers can be positive or negative. In Swift, integers are represented using the Int
type. The Int
type represents both positive as well as negative values. If you only need to store positive values, you can use the unsigned integer UInt
type. The size of an Int
type depends on the system on which your code is running. On 32-bit systems, Int
and UInt
each use 32 bits for storage, whereas on 64-bit systems Int
and UInt
each use 64 bits.
You can programmatically check the number of bytes stored by each data type using the sizeof()
function:
println("Size of Int: (sizeof(Int)) bytes")
println("Size of UInt: (sizeof(UInt)) bytes")
If you run the preceding statement on an iPhone 5 (which uses the 32-bit A6 chip), you will get the following:
Size of Int: 4 bytes
Size of UInt: 4 bytes
If you run the preceding statement on an iPhone 5s (which uses the 64-bit A7 chip), you will get the following:
Size of Int: 8 bytes
Size of UInt: 8 bytes
If you do not know the type of data a variable is storing, you can use the sizeofValue()
function:
var num = 5
println("Size of num: (sizeofValue(num)) bytes")
In most cases, you will use Int
for storing signed numbers, and UInt
if you do not need to store negative values (even if you don’t need to store negative numbers it is still a good idea to use Int
for code compatibility). However, if you want to explicitly control the size of the variable used, you can specify one of the various integer types available:
Int8
and UInt8
Int16
and UInt16
Int32
and UInt32
Int64
and UInt64
The following code snippet prints the range of numbers representable for each integer type:
//---UInt8 - Min: 0 Max: 255---
println("UInt8 - Min: (UInt8.min) Max: (UInt8.max)")
//---UInt16 - Min: 0 Max: 65535---
println("UInt16 - Min: (UInt16.min) Max: (UInt16.max)")
//---UInt32 - Min: 0 Max: 4294967295---
println("UInt32 - Min: (UInt32.min) Max: (UInt32.max)")
//---UInt64 - Min: 0 Max: 18446744073709551615---
println("UInt64 - Min: (UInt64.min) Max: (UInt64.max)")
//---Int8 - Min: -128 Max: 127---
println("Int8 - Min: (Int8.min) Max: (Int8.max)")
//---Int16 - Min: -32768 Max: 32767---
println("Int16 - Min: (Int16.min) Max: (Int16.max)")
//---Int32 - Min: -2147483648 Max: 2147483647---
println("Int32 - Min: (Int32.min) Max: (Int32.max)")
//---Int64 - Min: -9223372036854775808 Max: 9223372036854775807---
println("Int64 - Min: (Int64.min) Max: (Int64.max)")
For each integer type, the min
property returns the minimum number representable and the max
property returns the maximum number representable.
When you try to add two numbers of different integer types, you will get an error. Consider the following example:
var i1: UInt8 = 255
var i2: UInt16 = 255
var i3 = i1 + i2 //---cannot add two variables of different types---
To fix this, you need to typecast one of the types to be the same as the other type:
var i3 = UInt16(i1) + i2 //---i3 is now UInt16---
You can represent integer values as follows:
0b
prefix.0o
prefix.0x
prefix.The following code snippet shows the number 15 represented in the four forms:
let num1 = 15 //---decimal---
let num2 = 0b1111 //---binary
let num3 = 0o17 //---octal---
let num4 = 0xF //---hexadecimal---
You can pad the integers with zeros if you want to make them more readable. The preceding code snippet can be rewritten as the following statements without changing the value represented:
let num1 = 00000015 //---decimal---
let num2 = 0b001111 //---binary
let num3 = 0o000017 //---octal---
let num4 = 0x00000F //---hexadecimal---
In addition, for big numbers, you can also use underscores (_
) to make them more readable. For example, instead of writing one billion as:
let billion = 1000000000
you can use the underscore to make it more readable:
let billion = 1_000_000_000
The placement of the underscore is not important; the following represents the same value as the previous statement:
let billion = 100_00_00_00_0
Floating-point numbers are numbers with fractional parts. Examples of floating-point numbers are 0.0123, 2.45, and –4.521. In Swift, there are two floating-point types: Float
and Double
. Float
uses 32 bits for storage and Double
uses 64 bits. This can be confirmed using the sizeof()
function:
println("Size of Double: (sizeof(Double)) bytes")
println("Size of Float: (sizeof(Float)) bytes")
Double
has a precision of at least 15 decimal digits, while Float
has a precision of at least six decimal digits.
When assigning a floating-point number to a constant or variable, Swift will always infer the Double
type unless you explicitly specify otherwise:
var num1 = 3.14 – //---num1 is Double---
var num2: Float = 3.14 //---num2 is Float---
If you try to assign a Double
to a Float
type, the compiler will flag an error:
num2 = num1 //---num1 is Double and num2 is Float---
This is because the number stored in a Double
type may not be able to fit into a Float
type, thereby resulting in an overflow. In order to assign num1
to num2
, you need to explicitly cast num1
to a Float
, like this:
num2 = Float(num1)
When you add an integer constant to a Double
, the resultant type would also be a Double
type. Likewise, when you add an integer constant to a Float
, the resultant type would also be a Float
type, as the following example illustrates:
var sum1 = 5 + num1 //---num1 and sum1 are both Double---
var sum2 = 5 + num2 //---num2 and sum2 are both Float---
However, if you try to add Int
and Double
variables, you will get an error:
var i4: Int = 123
var f1: Double = 3.14567
var r = i4 + f1 //---error---
In order to add two variables of different types, you need to cast the Int
variable to Double
:
var r = Double(i4) + f1
When you add an integer to a floating-point number, the result would be a Double
value. For example:
var someNumber = 5 + 3.14
In the preceding statement, someNumber
would be inferred to be a Double
.
In Swift, for safety reasons there is no implicit type conversion—you must explicitly convert an Int
to a Float
(or Double
):
var f:Float
var i:Int = 5
f = i //---error---
f = Float(i)
When you cast a floating-point value to an integer, the value is always truncated—that is, you will lose its fractional part:
var floatNum = 3.5
var intNum = Int(floatNum) //---intNum is now 3---
You can represent floating-point values as follows:
0x
prefixThe following code snippet shows the floating-point number 345.678 represented in the two forms:
let num5 = 345.678
let num6 = 3.45678E2 // 3.45678 x 10^2
let num7 = 34567.8E-2 // 3.45678 x 10^(-2)
The E
(it can also be written as the lowercase “e
”) represents the exponent. 3.45678E2
means 3.45678
times 10
to the power of two.
You can also represent a hexadecimal floating-point number with an exponent of base 2:
let num8 = 0x2Cp3 // 44 x 2^3
let num9 = 0x2Cp-3 // 44 x 2^(-3)
In this case, 2Cp3
means 2C
(hexadecimal; which is 44 in decimal) times two to the power of three.
A type alias enables you to define an alternative name for the existing data type. For example, using the built-in types you can specify the data type for variables like this:
var customerID: UInt32
var customerName: String
However, it would be more useful if you could provide a more meaningful and contextually relevant name using the typealias
keyword:
typealias CustomerIDType = UInt32
typealias CustomerNameType = String
In the preceding code snippet, CustomerIDType
is now the alias for the UInt32
type, and CustomerNameType
is the alias for the String
type. You can use the aliases as if they are the data types, like this:
var customerID: CustomerIDType
var customerName: CustomerNameType
customerID = 12345
customerName = "Chloe Lee"
Swift supports the Boolean logic type—Bool
. A Bool
type can take either a true
or false
value.
The following code snippet shows the Bool
type in use:
var skyIsBlue = true
var seaIsGreen = false
var areYouKidding:Bool = true
skyIsBlue = !true //---skyIsBlue is now false---
println(skyIsBlue) //---false---
Bool
variables are often used in conditional statements such as the If statement:
if areYouKidding {
println("Just joking, huh?")
} else {
println("Are you serious?")
}
A tuple is an ordered collection of values. The values inside a tuple can be of any type; they need not be all of the same type. Consider the example in which you want to store the coordinates of a point in the coordinate space:
var x = 7
var y = 8
This used two variables to store the x and y coordinates of a point. Because these two values are related, it is much better to store them together as a tuple instead of two individual integer variables, as shown here:
var pt = (7,8)
In the preceding statement, pt
is a tuple containing two values: 7 and 8. You can also rewrite the tuple as follows:
var pt: (Int, Int)
pt = (7,8)
In this case, it is now obvious that the pt
is a tuple of type (Int, Int)
.
Here are some more examples of tuples:
var flight = (7031, "ATL", "ORD")
//---tuple of type (Int, String, String)---
var phone = ("Chloe", "732-757-2923")
//---tuple of type (String, String)---
If you want to retrieve the individual values inside a tuple, you can assign it to individual variables or constants:
var flight = (7031, "ATL", "ORD")
let (flightno, orig, dest) = flight
println(flightno) //---7031---
println(orig) //---ATL---
println(dest) //---ORD---
If you are not interested in some values within the tuple, use the underscore (_
) character in place of variables or constants:
let (flightno, _, _) = flight
println(flightno)
Alternatively, you can also access the individual values inside the tuple using the index, starting from 0:
println(flight.0) //---7031---
println(flight.1) //---ATL---
println(flight.2) //---ORD---
Using the index to access the individual values inside a tuple is not intuitive. A better way is to name the individual elements inside the tuple:
var flight = (flightno:7031, orig:"ATL", dest:"ORD")
Once the individual elements are named, you can access them using those names:
println(flight.flightno)
println(flight.orig)
println(flight.dest)
Swift uses a new concept known as optionals. To understand this concept, consider the following code snippet:
let str = "125"
let num = str.toInt()
Here, str
is a string, and the String
type has a method named toInt()
that converts a String
to an integer. However, the conversion may not always be successful (the string may contain characters that cannot be converted to a number) and the result returned to num
may be an Int
value or nil
. Hence, by type inference, num
is assigned a type of Int?
.
The ?
character indicates that this variable can optionally contain a value—it might not contain a value at all if the conversion is not successful (in which case num
will be assigned a nil
value). In the preceding code snippet, any attempt to use the num
variable (such as multiplying it with another variable/constant) will result in a compiler error—”value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
”:
let multiply = num * 2 //---error---
To fix this, you should use the If statement to determine whether num
does indeed contain a value. If it does, you need to use the !
character after the variable name to use its value, like this:
let str = "125"
let num = str.toInt()
if num != nil {
let multiply = num! * 2
println(multiply) //---250---
}
The !
character indicates to the compiler that you know that the variable contains a value and you indeed know what you are doing.
In the previous example, num
is an optional due to type inference. If you want to explicitly declare a variable as an optional type, you can append the ?
character to the type name. For example, the following statement declares description
to be an optional string type:
var description: String?
You can assign a string to description
:
description = "Hello"
You can also assign the special value nil
to an optional type:
description = nil
In the previous section you saw the use of the optional type and the use of the !
character to unwrap the value of an optional variable. The problem with this is that you likely will end up with a lot of !
characters in your code whenever you access the value of the optional variable. To access the value of an optional variable without using the !
character, you can declare an optional type as an implicitly unwrapped optional.
Consider the following declaration:
//---implicit optional variable---
var str2: String! = "This is a string"
Here, str2
is an implicitly unwrapped optional. When you access str2
, there is no need to use the !
character, as it is implicitly unwrapped:
println(str2) // "This is a string"
If str2
is set to nil
, accessing the str2
will return a nil
:
str2 = nil
println(str2) // nil
Many times you need to assign the value of an optional type to another variable or constant. Consider the following example:
var productCode:String? = getProductCode("Diet Coke")
if let tempProductCode = productCode {
println(tempProductCode)
} else {
println("Product Code not found")
}
In this snippet, getProductCode()
is a function that takes in a product name (of String
type) and returns a product code (a String
value) or nil
if the product cannot be found. As such, the productCode
is an optional String
.
To assign the value of productCode
to another variable/constant, you can use the following pattern:
if let tempProductCode = productCode {
Here, you are essentially doing this: check the value of productCode
; if it is not nil
, assign the value to tempProductCode
and execute the If block of statements—otherwise, execute the Else block of statements.
You can easily test this by setting productCode
to a value:
productCode = "12345"
if let tempProductCode = productCode {
println(tempProductCode)
} else {
println("Product Code not found")
}
The preceding code snippet will print out:
12345
If you now set productCode
to nil
:
productCode = nil
if let tempProductCode = productCode {
println(tempProductCode)
} else {
println("Product Code not found")
}
The preceding code snippet will print out:
Product Code not found
So far you have learned that you can use the !
character to unwrap an optional type’s value. Consider the following scenario:
var str:String?
var empty = str!.isEmpty
From this code snippet, str
is an optional String
and isEmpty
is a property from the String
class. In this example, you want to know if str
is empty, so you call the isEmpty
property. However, the preceding code will crash, as str
contains nil
, and trying to call the isEmpty
property from nil
results in a runtime error. The use of the !
character is like telling the compiler: I am very confident that str
is not nil
, so please go ahead and call the isEmpty
property. Unfortunately, str
is indeed nil
in this case.
To prevent the statement from crashing, you should instead use the ?
character, as follows:
var empty = str?.isEmpty
The ?
character tells the compiler: I am not sure if str
is nil
. If it is not nil
, please call the isEmpty
property; otherwise, ignore it.
An enumeration is a user-defined type consisting of a group of named constants. The best way to explain an enumeration is to use an example. Suppose you want to create a variable to store the color of a bag. You can store the color as a string, like this:
var colorOfBag = "Black"
The color can also be changed to, for example, “Yellow”:
colorOfBag = "Yellow"
However, using this approach is not safe, as there are two potential pitfalls:
In either case, it is always better to be able to define your own type to represent all the different colors that a bag may be. In this case, you create an enumeration containing all the valid colors. The following code snippet defines an enumeration named BagColor
:
enum BagColor {
case Black
case White
case Red
case Green
case Yellow
}
The BagColor
enumeration contains five cases (also known as members): Black
, White
, Red
, Green
, and Yellow
. Each member is declared using the case
keyword. You can also group the five separate cases into one single case, separated using commas (,
), as shown here:
enum BagColor {
case Black, White, Red, Green, Yellow
}
You can now declare a variable of this enumeration type:
var colorOfBag:BagColor
To assign a value to this variable, specify the enumeration name, followed by its member:
colorOfBag = BagColor.Yellow
You can omit the enumeration name by simply specifying its member name:
colorOfBag = .Yellow
Enumerations are often used in Switch statements. The following code snippet checks the value of colorOfBag
and outputs the respective statement:
switch colorOfBag {
case BagColor.Black:
println("Black")
case BagColor.White:
println("White")
case BagColor.Red:
println("Red")
case BagColor.Green:
println("Green")
case BagColor.Yellow:
println("Yellow")
}
Because the type of colorOfBag
(which is BagColor
) is already known, Swift allows you to specify only the enumeration members and omit the name:
switch colorOfBag {
case .Black:
println("Black")
case .White:
println("White")
case .Red:
println("Red")
case .Green:
println("Green")
case .Yellow:
println("Yellow")
}
One of the common operations that you need to perform with enumerations is that of associating a value with the members of an enumeration. For example, suppose you want to store the value of colorOfBag
to a file as a string (or, if you like, an integer). In this case, Swift makes it very easy for you to associate a value to members of an enumeration:
enum BagColor: String {
case Black = "Black"
case White = "White"
case Red = "Red"
case Green = "Green"
case Yellow = "Yellow"
}
After the declaration of the enumeration, append the enumeration name with a colon (:) and indicate the type of data to which you want each member associated (all members must be of the same type):
enum BagColor: String {
Within the enumeration, you then assign each member to the desired value, of the type that you have just specified:
case Black = "Black"
case White = "White"
case Red = "Red"
case Green = "Green"
case Yellow = "Yellow"
To obtain the value of an enumeration, use the rawValue
property of the enumeration instance:
var colorOfBag:BagColor
colorOfBag = BagColor.Yellow
var c = colorOfBag.rawValue
println(c) //---prints out "Yellow"---
The rawValue
property will return the value that you have assigned to each member of the enumeration.
What about the reverse? If you have a string of "Green"
, how do you convert it to the enumeration member? You can do so via the rawValue
initializer, as follows:
var colorOfSecondBag:BagColor? = BagColor(rawValue:"Green")
The preceding statement uses the rawValue
initializer to try to convert the string "Green"
to the enumeration member from BagColor
. Because the rawValue
initializer does not guarantee that it is able to return an enumeration member (imagine you pass in a value of, for example, "Brown"
), it returns an optional value—hence, the ?
sign in the statement. Once the value is returned, you can proceed to use it:
if colorOfSecondBag == BagColor.Green {
...
}
If you want to use the rawValue
property on colorOfSecondBag
, you should confirm that it is not nil
before proceeding to use it:
//---print only if colorOfSecondBag is not nil---
if colorOfSecondBag != nil {
println(colorOfSecondBag!.rawValue)
}
You also need to have a !
character to force unwrap the value of colorOfSecondBag
before accessing the rawValue
property.
In the previous section you saw that you could assign string values to each member in an enumeration. Very often, you would also assign integer values instead of strings. A good example is when you are representing the day of a week, as shown in the following code snippet:
enum DayOfWeek: Int {
case Monday = 1
case Tuesday = 2
case Wednesday = 3
case Thursday = 4
case Friday = 5
case Saturday = 6
case Sunday = 7
}
From the preceding statements, you can see that each day of the week is assigned an integer value—Monday is assigned 1, Tuesday is assigned 2, and so on. The following statements show how it can be used:
var d = DayOfWeek.Wednesday
println(d.rawValue) //---prints out 3---
When integer values are used for raw values within an enumeration, they are automatically incremented if no values are specified for subsequent members. For example, the following code snippet shows that only the first member within the DayOfWeek
enumeration is set to a value:
enum DayOfWeek: Int {
case Monday = 1
case Tuesday
case Wednesday
case Thursday
case Friday
case Saturday
case Sunday
}
Due to auto-incrementing of integer raw values, the following will still work:
var d = DayOfWeek.Thursday
println(d.rawValue) //---prints out 4---
The previous section demonstrated how you can assign a value to each member of an enumeration. Sometimes, it would be very useful to be able to store a particular value (or values) associated with a particular member of an enumeration. Consider the following code snippets:
enum NetworkType: String {
case LTE = "LTE"
case ThreeG = "3G"
}
enum DeviceType {
case Phone (NetworkType, String)
case Tablet(String)
}
The first enumeration, NetworkType
, represents the type of network to which a phone can connect. The second enumeration, DeviceType
, represents two types of devices: Phone
or Tablet
. If a device is a phone, you would want to store some values associated with it—in this case, you want to store its network type and the model of the device. If a device is a tablet, you would just store the model of the device.
To use the preceding enumerations declared, take a look at the following code snippet:
var device1 = DeviceType.Phone(NetworkType.LTE, "iPhone 5S")
var device2 = DeviceType.Tablet("iPad Air")
For device1
, its type is a phone and you store the associated information (network type and model name) with it. For device2
, its type is a tablet and you store its model name with it.
You can use a Switch statement to extract the associated value of an enumeration:
switch device1 {
case .Phone(let networkType, let model):
println("(networkType.rawValue) - (model)")
case .Tablet(let model):
println("(model)")
}
The preceding code snippet will output the following line:
LTE - iPhone 5S
You can define a function within an enumeration. Using the same example used in the previous section, we’ll now add a function named info
to the DeviceType
enumeration:
enum DeviceType {
case Phone (NetworkType, String)
case Tablet(String)
var info: String {
switch (self) {
case let .Phone (networkType, model):
return "(networkType.rawValue) - (model)"
case let .Tablet (model):
return "(model)"
}
}
}
In the preceding code snippet, the info()
function returns a string. It checks the member that is currently selected (using the self
keyword) and returns either a string containing the network type and model (for phone) or simply the model (for tablet). To use the function, simply call it with the enumeration instance, as shown here:
println(device1.info) //---LTE - iPhone 5S---
println(device2.info) //---iPad Air---
In this chapter, you had a more detailed look at the basic data types supported by Swift. In addition, you also learned about some of the features that make Swift a type-safe language. In addition, Swift also introduces some new features, such as optional types, as well as tuples. Enumerations in Swift have also been greatly enhanced with the support for raw values, associated values, as well as internal functions.
Consider the following code snippet. The compiler generates an error. Suggest ways to fix it.
var weightInPounds = 154
var heightInInches = 66.9
var BMI = (weightInPounds / pow(heightInInches,2)) * 703.06957964
println(BMI)
Examine the following code snippet:
enum cartoonCharacters: Int {
case FelixTheCat = 1
case AngelicaPickles
case ThePowerpuffGirls
case SpiderMan = 9
case GeorgeOfTheJungle
case Superman
case Batman
}
What is the output for the following statements?
var d = cartoonCharacters.GeorgeOfTheJungle
println(d.rawValue)
d = cartoonCharacters.AngelicaPickles
println(d.rawValue)
Examine the following code snippet:
enum cartoonCharacters: Int {
case FelixTheCat
case AngelicaPickles
case ThePowerpuffGirls
case SpiderMan = 9
case GeorgeOfTheJungle
case Superman
case Batman
}
What is the output for the following statements?
var d = cartoonCharacters.GeorgeOfTheJungle
println(d.rawValue)
d = cartoonCharacters.AngelicaPickles
println(d.rawValue)
The following code snippets cause the compiler to generate an error. Fix it.
var isMember:Bool?
if isMember {
println("User is a member")
} else {
println("User is a not member")
}
Topic | Key Concepts |
Integers | Integers are represented using the Int and UInt types. You can also use specific-sized types, such as Int8 and UInt8 , Int16 and UInt16 , Int32 and UInt32 , or Int64 and UInt64 . |
Integers representations | Integers can be represented as decimal, binary, octal, or hexadecimal. |
Floating-point numbers | Floating-point numbers are represented using the Float or Double type. |
Floating-point numbers representations | Floating-point numbers can be represented as decimal or hexadecimal. |
Boolean values | A Boolean value is either true or false . |
Tuples | A tuple is an order collection of values. |
Optional types | An optional type variable can either contain a value or nil . |
Unwrapping optional variables | To unwrap the value of an optional variable, use the ! character. |
Implicitly unwrapped optionals | If you declare a type to be an implicitly unwrapped optional, there is no need to use the ! character to unwrap the type. |
Optional binding | Optional binding allows a value of an optional to be assigned to another variable directly. |
Unwrapping an optional using ?
|
If you are not sure if an optional variable is nil or not before calling its methods or properties, use the ? character |
Enumerations | An enumeration is a user-defined type consisting of a group of named constants. |
Enumeration raw values | You can assign a value to each member of an enumeration. |
Enumeration auto-increment values | You can assign an integer value to a member of an enumeration; the compiler will automatically increment the value and assign them to each subsequent members. |
Enumeration associated value | You can store a value to associate with a particular member of an enumeration. |
Enumeration functions | An enumeration can also contain a function within its definition. |
3.131.152.166