<![CDATA[iOS app development tutorials - naveenr]]>/Ghost 0.11Sun, 27 Jun 2021 09:25:11 GMT60<![CDATA[Swift String Demystified]]>/swift-string-demystified/7a7edfa9-cfb9-4126-a3c6-9e08300cf035Sat, 18 Mar 2017 10:37:59 GMTA basic knowledge of Unicode character set and UTF-8, UTF-16 and UTF-32 encoding would help to understand Swift Strings better. If you are not aware of these, please consider reading /unicode-character-set-and-utf-8-utf-16-utf-32-encoding/.

This tutorial is updated for Swift 3.

Swift Strings Representation

Swift Strings are a collection of characters. For example "Hello World", "Señor" or "1 Infinite Loop, Cupertino" are all Swift strings.

Strings are represented using the String type in Swift.

var a: String = "Hello World"  
let b = "Welcome to the Club"  

Swift strings are Unicode compliant under the hood. This ensures that any character from any language can be used in swift.

The code below contains a String with acute accent ´.

var unincodeSample = "Café"  
print(unincodeSample) // prints Café  


String Mutability

Strings can be created to be mutable or immutable using the var or let keyword respectively. Mutable strings can be changed after they are declared, whereas this is not possible in immutable strings as they are constants.

Lets look at an example where we declare a mutable string and then append another string to it

var name = "Steve"  
name += " Jobs" // appending Jobs to name  
print(name) // Steve Jobs is printed  

The + operator is used to append two strings.

let declares immutable strings. Once a string is declared using let, no more modifications are allowed on that string.

let city = "New York"  
city += " ,USA" // compilation error  


Empty Strings

Empty strings can either be declared either using two empty quotes "" or using a string initializer.

var name = ""  
var address = String()  

We can append new strings to the empty strings.

var name = ""  
var address = String()  
name += "Steve Jobs"  
address += "Cupertino"  
print("Name:",name, ", Address:",address) //"Name: Steve Jobs , Address: Cupertino" is printed  


Testing for empty string

Strings can be tested for emptiness using the isEmpty property.

var nowhere = ""  
if nowhere.isEmpty {  
    print("nowhere is empty")
}

The above program outputs "nowhere is empty"

String Interpolation

String interpolation is a simple way to create new strings by inserting other Strings, Integers, Floats and so on. String interpolation is achieved by placing the item which we want to insert into the string between a backslash followed by a opening and closing paranthesis \()

Lets look at how this works with an example. Lets assume that we have the price of the book and we want to prefix the price with a "$" symbol. This can be achieved string interpolation.

let price = 250  
let currency = "$"  
let displayPrice = "\(currency)\(price)"//interpolation  
print("price of the book is",displayPrice)  

The above program outputs price of the book is $250. The displayPrice property is created by interpolating currency \(currency) and price \(price)

Lets write another program which prints the multiplication tables of a number using interpolation.

var number = 8  
for i in 1...10 {  
    print("\(number) times \(i) is","\(number * i)")
}

The above uses interpolation for displaying number \(number), i \(i) and the product of number and i \(number * i)

This program outputs

8 times 1 is 8
8 times 2 is 16
8 times 3 is 24
and so on.

Unicode Scalars

Swift strings can also be constructed using unicode scalars. A unicode scalar is nothing but a unicode code point represented using UTF-32 encoding. The syntax for constructing a string from a unicode scalar is \u{n} where n is the hexadecimal code point.

Lets construct a string from a unicode scalar.

The Unicode code point of A is U+0041. Using this code point, the String A can be constructed using the format \u{0041}

var a = "\u{0041}" //string created using unicode scalar  
print(a) // Prints **A**  

The above program outputs A.

The program below creates our beloved Hello World string using unicode scalars.

let beloved = "\u{0048}\u{0065}\u{006C}\u{006C}\u{006F} \u{0057}\u{006F}\u{0072}\u{006C}\u{0064}"  
print(beloved) //prints Hello World  


Different combinations of unicode scalars can produce the same character.

Lets look at what I mean with an example.

let nTilde = "\u{00F1}" // prints ñ  
let nTildeCombination = "\u{006E}\u{0303}" // This also prints ñ  

In the above program the latin alphabet ñ is constructed using the code point U+00F1 in the first line.

The other way of creating ñ is to use the code point for n U+006E and then follow it with the code point for ~ U+0303. Combining both into a single string "\u{006E}\u{0303}" also results in the creation of ñ

Accessing the Unicode Scalars of a String

The Unicode Scalars of a string can be accessed using the unicodeScalars property. Lets write a program that uses this property to print the unicode scalars of a string.

let enjoy = "Let's go 🏊"  
enjoy.unicodeScalars.forEach {  
    let hex = String(format:"%X",$0.value)
    print(hex, terminator: " ")
}

The above program uses the value property of UnicodeScalar which provides the numeric representation of the unicode scalar. It is converted to hexadecimal using "%X" format specifier and printed. The terminator parameter in the print is used to specify space as the terminator for print instead of the default newline.

Character

A character is a single unicode scalar or a group of unicode scalars that form a single human readable letter.

Character is represented using the Character type in swift

Lets let look at some codes sample to better understand Character. The code point of A is U+0041

let aChar: Character = "\u{0041}"  
print(aChar) //prints A  

In the above example, the single unicode code point \u{0041} forms the human readable character A. Hence aChar is a character.

The french letter é is formed by the combination of code points of e represented by U+0065 followed by acute accent ´ represented by U+0301

let acuteAccent: Character = "\u{0065}\u{0301}"  
print(acuteAccent) //prints é  

In the above example, two code points U+0065 and U+0301 combine and form the single human readable character . Hence acuteAccent is a Character and its value is

Characters can also be created using the human readable representation directly.

let aHumanReadable: Character = "A"  
let acuteAccentHumanReadable: Character = "é"  
print(aHumanReadable, acuteAccentHumanReadable)// prints A é  

The above example is pretty straight forward. It prints A and

Accessing characters of a string

String type has a characters property which provides access to all the characters of a string. This property can be used to iterate through the individual characters of a string.

Lets write some code to understand characters better.

let happy = "I ate 🍕 at a restaurant near my home"  
for char in happy.characters {  
    print(char, terminator: "")
}

In the above program happy.characters returns the collection of characters of the property happy and it is iterated using a for loop and printed. This program outputs

I ate 🍕 at a restaurant near my home

Finding the length a string

The length of the string is the total number of human readable characters in that string i.e. the number of Swift Character types in that String.

The length of the string can be found using the count property on the characters collection of the string.

let mystring = "Hello World"  
print("length is ",mystring.characters.count)  

The above program outputs length is 11

Lets look at one more example to make our understanding of characters and string length more clear.

let senorHumanReadable = "Señor" //Señor  
let senorUnicode = "Se\u{00F1}or" //this is also Señor  
let senorExtendedUnicode = "Se\u{006E}\u{0303}or" //even this is Señor

print("length of senorHumanReadable =",senorHumanReadable.characters.count)//prints 5  
print("length of senorUnicode =", senorUnicode.characters.count)// prints 5  
print("length of senorExtendedUnicode =", senorExtendedUnicode.characters.count)// prints 5  

In the above code the length of all three strings senorHumanReadable, senorUnicode, senorExtendedUnicode is the same and it is 5.

As we know already, the character ñ in the String Señor can be represented either as ñ or using Unicode Scalar as \u{00F1} or combination of Unicode Scalars \u{006E}\u{0303}

So all these three refer to the same human readable string Señor. Hence all of them are of the same length 5.

String Indices

As we already know swift strings are a collection of characters. A character can be a single unicode scalar or a combination of unicode scalars. Characters are not fixed size and can occupy any amount of memory.

The english alphabet A will occupy 1 byte whereas the emoji 😭 will occupy 4 bytes if encoded using UTF-8. To search and replace A with some other character, only 1 byte has to be changed, whereas if the emoji 😭 has to be replaced with some other character, 4 bytes has to be changed. This is not possible if integers are used to index strings. If integers are used to index strings, it is assumed that all characters occupy 1 byte. Say we start indexing a string using integer incrementation and get the first character from the 0th byte. If we try to get the second character of the string using the 1st byte, we might end up getting a wrong value since the first character may have occupied 2 bytes. This is where index type comes into picture.

Each String in Swift has a Index type which gives us the position of the character in a string.

The startIndex property returns the position of the first character in the string.

The endIndex property returns the position after the last character of the string. Hence accessing a character at endIndex will result in an error.

Lets look at some operations that can be performed on strings to make our understanding of indexes more clear.

String Operations

Accessing the first character of a string

let myString = "Hello World"  
let firstChar = myString[myString.startIndex] //H  

myString.startIndex will return the index of the first character in the string. We use the subscript syntax myString[myString.startIndex] to get the first character of a string.

Accessing the last character of a string

let myString = "My Café"  
let lastIndex = myString.index(myString.endIndex, offsetBy: -1) //index of the last character  
let lastChar = myString[lastIndex] //é  

myString.endIndex gives the index after the last character of the string. The method index(String.Index, offsetBy: String.IndexDistance) offsets the index by the number that is specified in the offsetBy: parameter. We pass the endIndex to this method and offset it by -1 to get the index of the last character of the string.

Indices of all characters of a string

The indices property of the characters property returns the index of each and every character of a string

let senor = "Hello Señor"  
for index in senor.characters.indices {  
    print(senor[index], terminator: "")// prints Hello Señor
}

In the above program senor.characters.indices is iterated using a for loop and every character of the string is printed using the subscript sytax senor[index]

Inserting characters into a string

The String method func insert(Character, at: String.Index) is used to insert a single character at position String.Index

Lets write a program to insert a character at the first position of the string.

var testString = "ello world"  
testString.insert("H", at: testString.startIndex)//test String is now "Hello world"  

The above program inserts H in the first position of the string.

Lets insert a character at the end of a string.

var food = "Pizz"  
food.insert("a", at: food.endIndex) //food is now Pizza  

food.endIndex gives the position right after the end of food and a is inserted in that position. The result is Pizza.

Consider the String "iPhone runs OS". Lets write a program to convert this string to "iPhone runs iOS".

var phoneOS =  "iPhone runs OS"

if let range = phoneOS.range(of: "OS") {  
    if let position = phoneOS.range(of: "OS")?.lowerBound {
        phoneOS.insert("i", at: position) // now phoneOS is "iPhone runs iOS"
    }

}

The range(of:) method returns a range Structure which contains the starting index of the String "OS" in the String "iPhone runs OS" and this can be accessed using the lowerBound property. Now that we have the position to be inserted, we use the method func insert(_ newElement: Character, at i: String.Index) to insert the character "i" at the appropriate position and this results in the String "iPhone runs iOS"

So far we have seen methods that insert a single character into a string. Now lets look at methods that insert a String into a String.

Lets write a program to insert a string at the beginning of another string.

var awesomeLanguage = "is a awesome language"  
awesomeLanguage.insert(contentsOf: "Swift ".characters,  
                       at: awesomeLanguage.startIndex) //Swift is a awesome language

The insert(contentsOf:at:) function inserts a collection of characters at a specified index. We convert the String "Swift " to a collection of characters and insert it at the start position of the string and we get the output "Swift is a awesome language"

Search and replace

A common operation is to search for a substring within a String and to replace it with some other string. We will replace "oranges" in the string "I love oranges a lot" with "apples"

Lets see how this is done in Swift.

var loveFood = "I love oranges a lot"  
if let rangeOfOranges = loveFood.range(of: "oranges") {  
    loveFood.replaceSubrange(rangeOfOranges, with: "apples")
}

The method func replaceSubrange(_ bounds: Range, with newElements: String) takes a range as input and replaces the string in that range with the given string. Here we pass the range of oranges and replace it with apples

Substrings of a String

The following program print all the substrings of a string.

var book = "Dan Brown is the Author of Da Vinci Code"  
for substring in book.components(separatedBy: " ") {  
    print(substring)
}

The components(separatedBy: String) method returns an array of strings which contains substrings that have been divided by the separator. The above program outputs,

Dan
Brown
is
the
Author
of
Da
Vinci
Code

Remove characters in a String

The remove(at: String.Index) method removes the character at the specified index.

The following program removes the last character of a string

var singular = "Pizzas"  
singular.remove(at: singular.index(before: singular.endIndex))  
print(singular)  

Appending characters to a String

One other common operation performed on Strings is to append a single character and also to append strings to existing String. Lets look at how this is done.

The + operator is used to append strings and create a new one.

var sentence = "Cars make" + " commuting easier"  
print(sentence) //Cars make commuting easier  

The append(_:) method can also be used to append characters and strings.

var health = "Apple"  
health.append("s")  
health.append(" are good for health")  

In the above program, first the character "s" is appended to testString and then the String " are good for health" is appended to testString.

String Comparison

Strings are compared using the == operator. Two strings are considered equal if their human readable representations are the same.

Lets look at an example.

var unicodeScalarAccent = "I was at the caf\u{E9}"  
var humanReadableAccent = "I was at the café"  
if unicodeScalarAccent == humanReadableAccent {  
    print("both the strings are equal")
}

In the above example both the Strings represent the same human readable text. Hence they are both equal.

Thats it for the basics of Strings. I recommend reading the Swift String Documentation which has tons of useful methods that will make your life easier when it comes to string processing.

Thanks for reading and please leave your thoughts in the comments section.

]]>
<![CDATA[Unicode Character Set and UTF-8, UTF-16, UTF-32 Encoding]]>/unicode-character-set-and-utf-8-utf-16-utf-32-encoding/8bdd8fae-8446-4de2-9c41-51a03b26cd75Sat, 18 Mar 2017 06:24:07 GMTASCII

In the older days of computing, ASCII code was used to represent characters. The English language has only 26 alphabets and a few other special characters and symbols.

The table below provides the ASCII characters and their corresponding Decimal and Hex values.

ascii codes

As you can infer from the above table, the ASCII values can be represented from 0 to 127 in the decimal number system. Lets look at the binary representation of 0 and 127 in 8 bit bytes.

0 is represented as

0 in binary

127 is represented as

127 in binary

It can be inferred from the above binary representation that decimal values 0 to 127 can be represented using 7 bits leaving the 8th bit free.

This is where things started getting messy.

People came up with different ways of using the remaining eight bit which represented decimal values from 128 to 255 and collisions started to happen. For instance the decimal value 182 was used by the Vietnamese to represent the Vietnamese alphabet ờ whereas the same value 182 was used by the Indians to represent the Hindi alphabet . So if an email written by an Indian contains the alphabet and if it is read by a person in Vietnam it would appear as . Cleary not the intended way to appear.

This is where Unicode character set came to save the day.

Unicode and Code Points

Unicode character set mapped each character in the world to a unique number. This ensured that there are no collisions between alphabets of different languages. These numbers are platform independent.

These unique numbers are called as code points in the unicode terminology.

Lets see how they are referred.

The latin character is referred using the code point

U+1E4D  

U+ denotes unicode and 1E4D is the hexadecimal value assigned to the character ṍ

The English alphabet A is represented as U+0041

Please visit http://www.unicode.org/charts/ to know the code points for all languages and alphabets of the world

UTF-8 Encoding

Now that we know what is unicode and how each alphabet in the world is assigned to a unique code point, we need a way to represent these code points in the computer's memory. This is where character encodings come into the picture. One such encoding scheme is UTF-8.

UTF-8 encoding is a variable sized encoding scheme to represent unicode code points in memory. Variable sized encoding means the code points are represented using 1, 2, 3 or 4 bytes depending on their size.

UTF-8 1 byte encoding

A 1 byte encoding is identified by the presence of 0 in the first bit.

UTF-8 1 byte encoding

The English alphabet A has unicode code point U+0041. It's binary representation is 1000001.

A is represented in UTF-8 encoding as

01000001

The red 0 bit indicates that 1 byte encoding is used and the remaining bits represent the code point

UTF-8 2 byte encoding

The latin alphabet ñ with code point U+00F1 has binary value 11110001. This value is larger than the maximum value that can be represented using 1 byte encoding format and hence this alphabet will be represented using UTF-8 2 byte encoding.

2 byte encoding is identified by the presence of the bit sequence 110 in the first bit and 10 in the second bit.

The binary value of the unicode code point U+00F1 is 1111 0001. Filling these bits in the 2 byte encoding format, we get the UTF-8 2 byte encoding representation of ñ shown below. The filling is done starting with the least significant bit of the code point being mapped to the least significant bit of the second byte.

11000011 10110001

The binary digits in blue 11110001 represent the code point U+00F1's binary value and the ones in red are the 2 byte encoding identifiers. The black coloured zeros are used to fill up the empty bits in the byte.

UTF-8 3 byte encoding

The latin character with code point U+1E4D is be represented using 3 byte encoding as it is larger than the maximum value that can be represented using 2 byte encoding.

A 3 byte encoding is identified by the presence of the bit sequence 1110 in the first byte and 10 in the second and third bytes.

The binary value for the hex code point 0x1E4D is 1111001001101. Filling these bits in the above encoding format gives us the UTF-8 3 byte encoding representation of show below. The filling is done starting with the least significant bit of the code point mapped to the least significant of the third byte.

11100001 10111001 10001101

The red bits indicate 3 byte encoding, the black ones are filler bits and the blues represent the code point.

UTF-8 4 byte encoding

The Emoji 😭 has unicode code point U+1F62D. This is bigger than the maximum value that can be represented using 3 byte encoding and hence will be represented using 4 byte encoding.

4 byte encoding is identified by the presence of 11110 in the first byte and 10 in the subsequent second, third and fourth bytes.

The binary representation of U+1F62D is 11111011000101101. Filling these bits in the above encoding format gives us the UTF-8 4 byte encoding of 😭. The least significant bit of the code point is mapped to the least significant bit of the fourth byte and so on.

11110000 10011111 10011000 10101101

The red bits identify the 4 byte encoding format, the blue ones are the actual code point and the black ones are the filler bits.

UTF-16 Encoding

UTF-16 encoding is a variable byte encoding scheme which uses either 2 bytes or 4 bytes to represent unicode code points. Most of the characters for all modern languages are represented using 2 bytes.

The latin alphabet ñ with code point U+00F1 and with binary value 11110001 is represented in UTF-16 encoding as

00000000 11110001

The above representation is in Big Endian Byte Order mode(Most significant bit first).

UTF-32 Encoding

UTF-32 encoding is a fixed byte encoding scheme and it uses 4 bytes to represent all code points.

The English alphabet A has unicode code point U+0041. It's binary representation is 1000001.

Its represented in UTF-32 encoding as shown below,

00000000 00000000 00000000 01000001

The blue bits are the binary representation of the code point. The above assumes Big Endian Byte order mode.

Thats if for character sets and encoding.

Thanks for reading. Please use the comments section for any feedback or queries.

]]>
<![CDATA[Beginning Container Views in iOS]]>/beginning-container-views-in-ios/850a6867-6255-4cbd-b5da-250dbf0dd8f2Thu, 26 Jan 2017 16:54:00 GMTWhat are container views

Container views allow a view to have child views which are in turn managed by their own view controllers. The view controllers such as Navigation Controller and Tab Bar Controller which are available in iOS by default fall under this category.

Today we will create a simple app which illustrates how to use container views and also how to use delegation to enable communication between the parent and the child view controllers.

The finished app will look like the pic below.

finished app

In the above app, the view that displays score is in its own container view and the tab bar controller is in a separate container view. When the question is answered correctly, the score will be updated accordingly.

This tutorial is written in Swift 3.0. Lets dive into coding.

Lets Get Started

Create a new Xcode project using Xcode->File->Project. Choose Single View Application and tap on Next.
single view application

Enter the Product Name, Organization Name and Organization Identifier. Choose Team as None, Language as Swift and Devices as Universal and tap on Next

new project

Choose a location to save the project and tap on "Create".

UI Design

Lets create the custom class for the view controller. Tap on ViewController.swift in the document outline and rename it to BaseViewController.swift

create class

Also change the class name from ViewController to BaseViewController in the source code

rename viewcontroller

Tap on the View Controller in the storyboard, then tap on Identity inspector and type BaseViewController as the class name and press enter

set custom class name

Adding Container View

Lets move on to adding container view to the story board. Choose Container View from the object library.

add container view from library 2

Drag and drop the container view from the object library on to the storyboard.

add container view from library 1

Tap on container view and add the following constraints using Add New Constraints at the bottom right corner. Set the top, left and right constraints to 0, uncheck Constraint to margins and tap on Add 3 constraints.

container view constraints 1

We have basically added 3 constraints that pin the container view to the top, left and right edges of the super view.

At this point you will get an error stating Container View Needs constraint for height. Lets add the missing constraint and make Xcode happy:).

Control + drag from the container view to the super view in the document outline and choose equal heights

container view constraints 2

container view constraints 3

Double tap on the newly added Equal Height constraint in the size inspector and change the multiplier to 1:5 and hit enter. This means that the container view will have one fifth of the height of its parent.

container view constraints 4

container view constraints 5

Tap on the base view controller, then tap update frames in the bottom right corner to update the frames in accordance with the constraints.

view update frames

After updating constraints, the storyboard will look like this.

view after update frames

Congrats, you have successfully created your first container view. As you can see above, the container view is backed by its own view controller and it resizes in accordance with the size of the container view.

Lets create the custom class for this container view controller. Tap on File->New->File... and choose Cocoa Touch Class and tap on Next.

new cocoa touch class 1

Type ScoreViewController in Class, UIViewController in Subclass, uncheck "Also create XIB file" and choose the language as Swift. Tap Next followed by Create

new cocoa touch class 2

Now tap on the container view's view controller in the storyboard and choose identity inspector from the top right.

set custom class

Type ScoreViewController in the Class Field and press Enter.

Now lets move on and create the bottom container view. Drag and the drop a second container view from the object library on to the storyboard.

create bottom container view

Now we have to delete the view controller of the bottom container view and replace it with a tab bar controller.

Go ahead and select the view controller of the bottom container view from the document outline. It will be named as View Controller Scene.

delete container view controller

Tap on it and press delete.

Drag a Tab Bar Controller from the Object library on to the story board

tab bar controller

Control + drag from the bottom container view to the tab bar controller and choose embed from the menu that pops up. The tab bar controller resizes itself to the size of the container view

embed tab bar in container view 1

embed tab bar in container view 2

Autolayout for the bottom container view

Now lets add the constraints for the bottom container view. Tap on the bottom container view in the storyboard to select it. Tap on Add New Constraints in the bottom right corner. Make the top, bottom, left and right spacing to zero, uncheck Constrain to margins, select Items of New Constraints in Update frames and tap on Add 4 Constraints

bottom container view constraints 1

The storyboard will look like this after adding constraints.

bottom container view constraints 2

Lets change the background color of the top container view to light gray and run the app. Select the root view of the SourceViewController and change its background color to light gray.

Run the app and you will see a screen like the one below.

curren app status

Score View Design

Drag a label from the Object library to Score View Controller Scene and change its text to Score

score label creation

Set the left, top and bottom constraints of the score label to zero. Uncheck Constrain to Margins, choose Items of New Constraints in Update Frames and then tap on Add 3 Constraints

score label constraints

Drag one more label to the right of the score label and set its text to 0.

zero label creation

Control + drag from zero to score and select horizontal spacing. You will get a few errors, which we will fix in the next step.

zero label constraints 1

zero label constraints 2

Select the zero label and the following constraints. Set top and bottom to 0, uncheck constrain to margins, select All frames in container and tap on Add 2 constraints

zero label constraints 3

Select the zero label, tap on size inspector, select the Leading space To: Equals: constraint, tap on edit, change its value to 10 and press enter.

zero label constraints 4

Tab Bar UI Design

Lets set the custom class for the first tab. Tap on File->New->File... Select Cocoa Touch Class. Enter the class name as StartupQuizViewController, subclass as UIViewController, uncheck Also create XIB File, choose language as Swift and tap on Next Followed by Create

Tap on Item 1 in the story board and set the custom class as StartupQuizViewController in the Identity Inspector

set custom class

Select Item 1 Tab bar item in the document outline, tap on Attributes Inspector and set the Title as Startup Quiz and press Enter

tab bar title change

Drag and drop a label on to the Startup Quiz tab. Change its text to Who is the founder of apple. Select the label and tap Align in the bottom right corner. Check Horizontally Center in Container and Vertically Center in Container, Select All Frames in Container as Update Frames and Tap on Add 2 Constraints

question label

Drag and drop 2 buttons from the Object Library on to the Startup Quiz tab one below the other below the label and change their text to Steve Jobs and Elon Musk

answer labels

Control + drag from Steve jobs button to Who is the founder of apple label. Select both Vertical Spacing and Center Horizontally from menu and tap on Add Constraints.

answer constraints 1

Select Steve Jobs button and change the Top Space to: Equals constraint to 8 in the size inspector and press Enter

answer constraints 2

Similarly Control + drag from Elon Musk button to Steve Jobs button and select both Vertical Spacing and Center Horizontally from the menu and tap on Add Constraints.

Select Elon Musk button and change the Top Space to: Equals: constraint in the size inspector to 8.

Tap on Editor->Update Frames menu to update all frames.

Lets quickly finish the second tab bar's design. Its similar to the first one.

Lets create the custom class for the second tab bar. Tap on File->New->File... and choose Cocoa Touch Class and tap on Next. Enter ScienceQuizViewController in Class, UIViewController in subclass, uncheck Also create XIB File and choose the Language as Swift and tap on Next followed by Create

Select the second tab in the document outline, it will be named as Item 2 and change the class to ScienceQuizViewController in the identity inspector.

set custom class

Select the tab bar item of the second tab in the document outline and change its title to Science Quiz in the attributes inspector and press Enter

tab bar bottom title change

Drag and drop a label from the object library to Science Quiz scene and and change its text to Who Invented the Electric Bulb

Select the label and tap Align in the bottom right corner and select Vertically Center in Container, Horizontally Center in Container, update frames as All Frames in Container and tap Add 2 Constraints

tab bar constraints 1

Drag 2 buttons below the label one below the other. Change their titles to Thomas Edison and Albert Einstein

add answer buttons

Lets add constraint to these two buttons. Control + drag from Thomas Edison To Who Invented Electric Bulb and choose both Vertical Spacing and Center Horizontally from the menu and tap on Add Constraints

Select Thomas Edison in the storybard, tap on size inspector and edit Top Space To: Equals: to 8 and press Enter

second quiz answer button constraints 1

Control + drag from Albert Einstein to Thomas Edison and select both vertical spacing and center horizontally in container and tap Add Constraints

Select Albert Einstein and change the Top Space To: Equals: constraint to 8 in the size inspector.

second quiz answer constraints 2

Select science quiz scene from the document outline and tap Editor->Update Frames to update all frames.

Go ahead and run the application. You should be able to see 2 tabs with a score view on the top.

Communication between Container Views

To update the score when the correct option is selected, we need to inform the parent view controller about it and then accordingly update the child view from the parent view. We can achieve this using delegation. In simple terms delegation allows 2 objects to communicate with one another blindly without know about each other.

Create a new swift file by tapping File->New->File... and select Swift File from the list and Tap next. Name the file as ScoreDelegate and tap on create.

Enter the following code in
ScoreDelegate.swift

import Foundation  
protocol ScoreDelegate:class {  
    func addOneToScore()
}

This delegate function will be used by the child view controllers StartupQuizViewController and ScienceQuizViewController to tell the parent BaseViewController to add one to the score.

Now that the delegate is ready, lets use it to pass data from the child view controllers.

Select the Startup Quiz Scene from the document outline and tap on Show Assistant Editor. Then select Automatic->StartupQuizViewController.swift.

Control + drag from steve jobs button to StartupQuizViewController and choose Action as connection and type correctAnswer in the name and then tap on connect.

create IBAction 1

create IBAction 2

This will create a Action like below,

@IBAction func correctAnswer(_ sender: Any) {
}

Lets create a variable for the delegate now. Add the following code to the first line of the StartupQuizViewController class

weak var delegate:ScoreDelegate?  

Now that we have the delegate ready, lets call it when someone taps on the correct answer. The following is the full code of StartupQuizViewController after calling the delegate method inside the correctAnswer Action

StartupQuizViewController.swift

//StartupQuizViewController.swift 
import UIKit

class StartupQuizViewController: UIViewController {  
    weak var delegate:ScoreDelegate?

    @IBAction func correctAnswer(_ sender: Any) {
        delegate?.addOneToScore()
    }
}

You might have viewDidLoad and didReceiveMemoryWarning methods in your class. You can leave them if you wish. I deleted them, since they are not going to be used.

Since Elon Musk is the wrong answer, we don't do anything when someone taps on it :)

Lets move on to the code for the Science Quiz tab and do the same job of calling the delegate when some one answers the science quiz correctly.

Select the science quiz scene in the document outline and tap on the Show Assistant Editor. Then select Automatic->ScienceQuizViewController.swift

Control + drag from Thomas Edison button to ScienceQuizViewController.swift. Select connection as Action and type correctAnswer in the Name field and tap connect. This will create the action below.

@IBAction func correctAnswer(_ sender: Any) {
}

Now lets create the delegate var and call the addOneToScore method when the correct answer is tapped. This is identical to the StartupQuizViewController. Here is the complete code.

ScienceQuizViewController.swift

//ScienceQuizViewController
import UIKit  
class ScienceQuizViewController: UIViewController {  
    weak var delegate: ScoreDelegate?

    @IBAction func correctAnswer(_ sender: Any) {
        delegate?.addOneToScore()
    }

}

Since Albert Einstein is the wrong answer, we don't do anything when that button is tapped.

The next step is to make BaseViewController as the delegate of these two view controllers so what it will know when to update the score.

Since the BaseViewController is the parent class for the Tab bar controller that holds StartupQuizViewController and ScienceQuizViewController view controllers, by overriding prepare(for segue: UIStoryboardSegue, sender: Any?) we can get the reference of the child tab bar controller and then we can set the BaseViewController as the delegate of StartupQuizViewController and ScienceQuizViewController. Move to BaseViewController and write the below code in that.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {  
    if let baseTabBarVC = segue.destination as? UITabBarController {
        if let firstTab = baseTabBarVC.viewControllers?.first as? StartupQuizViewController {
            firstTab.delegate = self
        }
        if let secondTab = baseTabBarVC.viewControllers?[1] as? ScienceQuizViewController {
            secondTab.delegate = self
        }
   }
}

Now lets create an extension and implement the delegate method. In BaseViewController add the following code

extension BaseViewController: ScoreDelegate {  
    func addOneToScore() {
        print("score to be updated")
    }
}

The complete code for BaseViewController is provided below.

BaseViewController.swift

import UIKit  
class BaseViewController: UIViewController {  
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let baseTabBarVC = segue.destination as? UITabBarController {
            if let firstTab = baseTabBarVC.viewControllers?.first as? StartupQuizViewController {
                firstTab.delegate = self
            }
            if let secondTab = baseTabBarVC.viewControllers?[1] as? ScienceQuizViewController {
               secondTab.delegate = self
            }
        }
    }
}

extension BaseViewController: ScoreDelegate {  
    func addOneToScore() {
        print("score to be updated")
    }
}

Now if you run the app and tap on either Steve Jobs in the Startup Quiz tab or Thomas Edison in the Science Quiz tab, you can see a console log score to be updated :)

We have successfully made communication from the child container view to its parent view, however we still need to update the score in the ScoreViewController If you have noticed, you can see that the score always remains zero even though we tap on the correct answer. Lets go ahead and fix this now.

We have to first create a outlet for the score label in ScoreViewController

Select ScoreViewController in the document outline and then tap on Show the assistant editor. Select Automatic->ScoreViewController.swift

Control + drag from 0 label to ScoreViewController. Choose Outlet as connection and type scoreLbl as Name and then click on connect.

create outlet

Next we will initialize a score variable to zero and also create a method to increment it when the score needs to be updated and update the score label with the score. The Complete ScoreViewController.swift code is provided below.

ScoreViewController.swift

import UIKit

class ScoreViewController: UIViewController {  
    var score = 0

    @IBOutlet weak var scoreLbl: UILabel!

    func updateScore() {
        score = score + 1
        scoreLbl.text = String(score)
    }
}

Now we have the method ready to update the score in the ScoreViewController. We have to call this method from the BaseViewController to update the score.

Create a variable scoreVC to hold ScoreViewController reference. We will populate this variable in the override func prepare(for segue: UIStoryboardSegue, sender: Any?) method as we did earlier. Then we will use this variable to call the score update method in ScoreViewController. The updated BaseViewController.swift is provided below.

BaseViewController.swift

import UIKit  
class BaseViewController: UIViewController {  
    var scoreVC:ScoreViewController?
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let baseTabBarVC = segue.destination as? UITabBarController {
            if let firstTab = baseTabBarVC.viewControllers?.first as? StartupQuizViewController {
                firstTab.delegate = self
            }
            if let secondTab = baseTabBarVC.viewControllers?[1] as? ScienceQuizViewController {
                secondTab.delegate = self
            }
        }
        if let vc = segue.destination as? ScoreViewController {
            scoreVC = vc
        }
    }
}

extension BaseViewController: ScoreDelegate {  
    func addOneToScore() {
        print("score to be updated")
        scoreVC?.updateScore()
  }
}

Now when we run the app and tap on either Steve Jobs in the Startup Quiz tab or Thomas Edison in the Science Quiz tab, we can see that the score is updated.

The complete project is available in github for download.

Please post your feedback and queries in the comments section. Thank you.

]]>