App Development Using iOS Icloud
App Development Using iOS Icloud
Shantanu Baruah
Shaurya Baruah
App Development Using iOS iCloud: Incorporating CloudKit with
Swift in Xcode
Shantanu Baruah Shaurya Baruah
Somerset, NJ, USA Somerset, NJ, USA
Acknowledgments�����������������������������������������������������������������������������xix
Introduction���������������������������������������������������������������������������������������xxi
v
Table of Contents
vi
Table of Contents
viii
Table of Contents
Table Setup��������������������������������������������������������������������������������������������������231
Top View Blocks�������������������������������������������������������������������������������������������243
Custom Delegation��������������������������������������������������������������������������������������������249
Define the Delegate Protocol�����������������������������������������������������������������������250
Implementing the Delegate�������������������������������������������������������������������������251
Calling the Delegate������������������������������������������������������������������������������������254
Summary����������������������������������������������������������������������������������������������������������256
ix
Table of Contents
x
Table of Contents
Navigation Bar���������������������������������������������������������������������������������������������361
Setup�����������������������������������������������������������������������������������������������������������363
Save Book����������������������������������������������������������������������������������������������������364
Update Book Database Functions����������������������������������������������������������������365
Summary����������������������������������������������������������������������������������������������������������367
xi
Table of Contents
xii
Table of Contents
xiii
Table of Contents
Index�������������������������������������������������������������������������������������������������495
xiv
About the Authors
Shantanu Baruah is an Executive Vice
President leading new business acquisition
in the Life Sciences and Healthcare business
at HCL Technologies. With over 21+ years
of experience across multiple disciplines,
Shantanu has been a pioneer in the fields
of healthcare, life sciences, and digital and
information technology at HCL Technologies.
His leadership has guided delivery, practice
building, and development of market-leading
solutions to reach new heights. A leader
with global exposure, Shantanu has successfully led teams across India,
Singapore, France, and the United States. His technological expertise
and innovative leadership qualities have placed him at the forefront
of important business acquisitions. Shantanu is active in the App
development community and has an approved App on Apple App Store.
He has been recognized as one of the top 25 Healthcare IT Executives of
2020 by the IT Services Report. He is also recognized as one of the Top 50
Healthcare Leaders in Consulting by The Consulting Report Magazine in
August 2022. Shantanu lives in New Jersey. His philanthropic outreach
includes education for children in developing nations.
xv
About the Authors
xvi
About the Technical Reviewer
Vishwesh Ravi Shrimali completed his
bachelor’s in mechanical engineering and
master’s in machine learning and artificial
intelligence. Currently, he is working at
Mercedes Benz Research and Development
as an ADAS Engineer. He has also coauthored
Machine Learning for OpenCV 4 (Second
Edition), Computer Vision Workshop, and
Data Science for Marketing Analytics (Second
Edition) by Packt. When he is not writing blogs
or working on projects, he likes to go on long
walks or play his acoustic guitar.
xvii
Acknowledgments
Writing a book on technology is quite a time-consuming task, for it is not
about writing your thoughts on paper but also writing code and validating
its completeness. I would like to thank my wife Kapila Sood and my
daughter Nitara Baruah for their undeterred support and patience as I put
together endless hours to bring this book to life. Their feedback on the
aesthetics of the App, which is an integral part of the book, and the outline
of the book content is invaluable.
I would also like to thank my mother Meenu Baruah who discovered
the writing itch in me quite early in my life and has always encouraged me
to pursue writing. While at times I remained uncertain, she never doubted
that one day I will be a published author. She is also the first published
author in our family, and I am certain I got my writing genes from her.
I would also like to thank my father, Late Sailender Nath Baruah, who
introduced me to the world of software back in the day when computers
were not ubiquitously available.
I cannot thank enough my Apress team for all their help in making
this book a reality. Aaron Black, who I pitched the book to, loved the idea
instantly and introduced me to the world of publishing. Susan McDermott,
for all the coaching and help in navigating through the publishing process;
Shonmirin, in helping with all coordination; Vishwesh Ravi Shrimali
and James Markham, for reviewing my code and making sure it works
as expected and for providing valuable comments in reorganizing the
chapters and content.
—Shantanu Baruah
xix
Acknowledgments
xx
Introduction
Over the last decade, the usage of iPhones has skyrocketed. Apple released
their first iPhone back in the summer of 2007. What started off as a small
idea, a handheld computer used to call and message people, has become
the new normal. Practically everyone carries a phone in their back-pocket.
The first smartphone, released in 1994, had just ten inbuilt Apps, including
Address Book, Calculator, Calendar, Mail, Notepad, and Sketch Pad,
among a few others. If you have an iPhone, you will probably realize that
you still have all these Apps. They have been around for over two decades!
The only difference today is that we have an App store, where we can
download about 2.2 million other Apps, if we so desire. This is a big jump
from the original ten.
Here is a scenario. You wake up one morning, pull out your iPhone,
and suddenly have an idea for a brilliant App, something humanity has
never seen before. However, you don’t know how to code, or where to even
start. If you have navigated your way to this book, you are probably in a
similar predicament. This book shouldn’t scare you. It is not a dictionary
filled with every single line of code that has ever existed. Rather, it is a
step-by-step guide, which will help you with everything you need to know
for creating a professionally appealing App, even if you are just a beginner
who has never written a line of code in your life.
The first part of the book will teach you the basics of coding as you will
build the initial version of your sample App (read the next section to learn
more about the App we will build). The second part of the book delves
deeper into exploring all additional features that the Apple ecosystem
offers, and we will create the professional version of the sample App.
xxi
Introduction
xxii
Introduction
take book notes, and share your reading list with friends. If you want to
change some of the details in your book, you can do that very easily with
the Update Feature. You can also delete a book whenever you like. In
summary, the book tracker App will have the following functions:
• Set reminders
xxiii
Introduction
xxiv
Introduction
UI using code (instead of Apple providing drag and drop interface) and
applying constraints for multiform display. For complex iPhone App
development, we recommend code-based UI creation and constraint
management, and though this is an advanced concept, we are introducing
this in Part I. At the end of Part I, we will have a basic functioning App.
This part also provides details on some core Swift language concepts.
This is a separate section for reference purposes only in case details
around a core concept need a revisit.
xxv
Introduction
there is lack of documentation available on this topic. This book will share
everything we learned along the way while making this App from scratch.
It will teach you everything you need to know in building iPhone App
CloudKit. If you are planning to create a native iOS application, this book
should help make that journey enriching and enjoyable.
The code is compatible with the latest iOS 15.0 version and is built
using Swift language in Xcode 13.3.1.
xxvi
PART I
Basic App
Development Using
Swift Core Concepts
CHAPTER 1
Xcode Introduction
In this chapter, you will learn about the platform we will use to write
our code. If you already have downloaded Xcode, and have a good
understanding of the system, feel free to skip this chapter and head to
Chapter 3 to learn about CloudKit.
About Xcode
For beginners, you will probably have no idea what Xcode is. No need to
worry, read through this short chapter, and you will be well versed with the
necessary Xcode concepts.
Apple has developed Xcode as its IDE platform for macOS users to
create native applications for all Apple devices. It was first released in
2003, and since then, it has received many updates. The latest edition is
version 13.3.1 (as of September 2021). It is a free download from the Mac
App Store; however, for a developer license, you need to pay around 100
dollars a year.
Although it may seem daunting at first, when you get used to
programming and the interface, Xcode is a brilliant platform for coding.
There are a few reasons why I fell in love with this IDE.
First, the auto-completion feature of Xcode is a great little tool
which will come in quite handy in writing your code. When you type any
character or phrase, a list of all the suggested functions pops up, so it’s
easy to find functions, their definition, and related elements, particularly
helpful if you are new to the syntax. Because of this feature, you don’t
have to spend a ton of time looking for help content. You can simply type
something like the name of the function and try finding it using the auto-
complete feature.
I would also like to mention Playground, which is a great feature often
overlooked. Use Playground to test out different codes and for learning the
basics of programming. Playground also will give you instant results; the
program is always running, so you can see the results of your code in the
console. There’s no need to click any button or refresh the project. This
way, you don’t waste a lot of your time worrying about code structure at an
early stage of learning. It is a great way to train, learn new concepts, and get
better at programming.
Apps which span across devices (iPhone, iPad, and iOS) and use
multiple form factors often will demand their screen to be designed using
code. However, Xcode also has a Storyboard, where you can easily drag and
drop to create your own screen. This feature works fine for a simple-looking
screen. We will start by making the screen with this feature, and later, we
will completely revamp the App by designing the screens using code.
Storyboard allows you to add any items to your screen, such as
labels, buttons, text fields, and so on. You can also change the color, text,
font sizes, and many more in the property’s screen. The reason most
programmers don’t use the storyboard in the final version of their App is
because when you create the screen using code, you can create complex
screen without worrying about overlapping objects and anchors, and it is
easier to draw the App the way you want it on different form factors of iOS
devices.
Apple released the language Swift in October 2014. Ever since then,
it has continued to grow, and it has become the most preferred language
for iOS coding. Prior to Swift, Apple had Objective-C as the primary
language. There are over 1.5 million jobs created around App design and
development since the launch of the App Store in 2008. The best part
about Swift is that it is not difficult for beginners to learn.
4
Chapter 1 Xcode Introduction
Before you get started with coding, consider reading the next section to
learn how you can install Xcode.
5
Chapter 1 Xcode Introduction
Interface Introduction
When you first launch Xcode, your screen should look something like this.
Your initial Xcode screen will have two columns. On the left side, you
will see three options that you can select right underneath the Xcode logo.
The first option will allow you to create an Xcode project. This is what we
will be using when we start creating our App. Underneath that, you will
see the button which allows you to clone an existing project. This is useful
when the project you are going to work on is quite like an existing project,
or when you need to do major changes to an already existing project.
If you select the third option, you will be able to search through files
on your computer. Most of the time, you will not need to use this feature,
because the right side of the screen will show the recent projects that you
have been working on. If you want to create a new playground, you will go
to File ➤ New ➤ Playground.
If you select Create a new Xcode Project, or go to File ➤ New ➤ Project,
it will ask you to select a template for your project. On the top of the screen,
6
Chapter 1 Xcode Introduction
you can choose what platform you would like to use (IOS, MacOS, etc.),
and you can choose which type of application you would like to make. For
the purposes of this book, we will select iOS, and the first option, App. If
you explore a little more, you will notice there are different types of Apps
too, such as games and messaging.
Selecting next will take you to the screen where you add additional
information, such as your product name and organization. Since we
are just going through the interface right now, feel free to fill out this
information however you want. Later, when we start building the Book
Tracker App, we can give more logical values and names. Make sure you
have selected “Storyboard” instead of Swift UI for the interface.
7
Chapter 1 Xcode Introduction
This is the initial screen you will see when you first launch your
application. On the left-hand side, you will notice a list of different files
which we will be using throughout our App creation. The folder Test Run-
Through is the name of the main App folder, which has many other files
within. The screen that is initially shown is where you will enter basic
settings. You can scroll through and add/change any details that you
desire. For now, we will stick to the default values.
Here is the Main Storyboard screen. We will spend a lot of time here
during the beginning stages of our application.
8
Chapter 1 Xcode Introduction
Chapter Summary
In this chapter, we learned about Xcode, the integrated development
environment (IDE) platform we will use to develop our iPhone App. The
chapter provided a step-by-step guide to install Xcode and provided the
basic understanding of the Xcode interface. It is highly recommended to
explore the interface and learn about the different screen layouts before
you begin coding.
9
CHAPTER 2
CloudKit Overview
Ever since Apple announced CloudKit, a new avenue for cloud-based App
development has emerged for developers. CloudKit offers a great avenue
for Apps that need interactions with other App users. Prior to CloudKit,
developers used to create homegrown solutions from authentication to
data sync, which is not an easy task to do besides being vulnerable to
security breaches. CloudKit wraps all such complexities and provides a
scalable architecture to develop data-persistent, cloud-based applications.
In this chapter, we will learn some basics, including setting up
CloudKit the dashboard, and managing CloudKit-based applications.
CloudKit at a Glance
CloudKit was first introduced in 2014 and, since then, has gone through
many revisions. It is a simple way to integrate your applications (iOS,
WatchOS, MacOS, and TVOS) to iCloud. Apple also uses CloudKit to
natively integrate its own family of products. For example, when you Share
a Note with another user, Apple is using CloudKit as Backend as a Service
(BaaS) to make that share happen. Following are some key offerings that
we will learn in future chapters of the book:
12
Chapter 2 CloudKit Overview
Setting Up CloudKit
Before you can explore the CloudKit functions, you need to configure the
same in the Xcode interface. After iCloud is enabled, an entitlement file
is created which is required to access any iCloud features. The setup of
CloudKit is a five-step process.
Step 1: Open the Xcode project for which you want to set up CloudKit
and click on the Project icon in the project Navigator. The project
properties window will appear as shown in Figure 2-1.
Step 2: Before you add CloudKit to your project, you need to sign into
iCloud. This is required because CloudKit persists all data in iCloud. iOS
uses the logged in user to authenticate the user for all your application
data transactions. On the top bar in the middle screen, click on Signing &
Capabilities. From the screen presented, click on the Team field; if you
don’t find your name, then click on add an account and login using your
Apple ID. Once logged in, your screen will look like in Figure 2-2.
13
Chapter 2 CloudKit Overview
Step 3: Now that you have logged in, you can add the CloudKit
capabilities. In the Screen, click + Capability. A popup screen will be
presented, search for iCloud. Once found, double click on iCloud to add
the capability. You will be presented with a screen, as shown in Figure 2-3.
14
Chapter 2 CloudKit Overview
15
Chapter 2 CloudKit Overview
Step 4: If Status shows warning. Click on sign and enter your Apple ID
to get the provision profile and the signing certificate. Once signed in, the
screen will look like the one shown in Figure 2-5:
The middle section of the preceding screen is zoomed in Figure 2-6 for
better clarity.
16
Chapter 2 CloudKit Overview
17
Chapter 2 CloudKit Overview
18
Chapter 2 CloudKit Overview
19
Chapter 2 CloudKit Overview
20
Chapter 2 CloudKit Overview
21
Chapter 2 CloudKit Overview
22
Chapter 2 CloudKit Overview
23
Chapter 2 CloudKit Overview
24
Chapter 2 CloudKit Overview
Figure 2-12 shows all system and custom fields of Book data type.
We are now ready with the initial table definition. Next, we will build
a screen to save a book record to the database. Please note, for queries to
work, we need to make the system field recordName queryable.
Summary
In this chapter, we learned about the fundamentals of CloudKit. We also
explored how to create a CloudKit-aware iOS App, and the steps we need
to follow to configure an iOS-native cloud-based database. Toward the end
of the chapter, we learned about CloudKit Dashboard, a convenient, user
friendly way to manage necessary CloudKit functions.
Now that we have covered the fundamentals of Swift programming, in
the next chapter, we will start building our Book Tracker App basic version.
25
CHAPTER 3
Variables
Learning to define and use variables are basics of Swift programming. We
are going to focus on the following variable definition types. This is not an
exhaustive list, but the important ones that we will need to develop our
App using the Swift language.
Basic Types
Types define what value a variable should store. The key types which we
will be mostly using in our Book Tracker Apps are
Types always come after the variable name separated by colon (“:”)
Please note if the type is not defined, then the variable automatically
assumes the type based on the value assigned. For example, in the
following line, the title variable will be a String variable, which the system
deciphers based on the string literal provided in the quotes.
28
Chapter 3 Core Swift Concepts
Example
NSObject is the native root class from which every subclass is inherited.
Once the class is defined, we need to define the object using var or let,
as explained in the following:
notice the constructor () after the class name to instantiate the class as
an object which is now referred as book.
29
Chapter 3 Core Swift Concepts
Structs
The syntax of struct definition is as follows:
Example
Array
Arrays are structures to hold multiple values of a particular kind of
element. Swift allows a single and multidimensional array of any type
(basic types, classes, or structures). In our Book Tracker App, we will need
a book array to hold all the book names that the user has read (a one
dimensional String Array) or a book details array holding book names,
author, and genre (a class object array).
How to define Arrays
30
Chapter 3 Core Swift Concepts
var author:[String] = []
class Book:NSObject{
var bookname:String!
var authorname:String!
var genre:String!
var status: String!
}
var books:[Book] = []
books variable is an array of type Book class, which can hold multiple
Book details (name, author, genre, and status)
Accessing an Array
31
Chapter 3 Core Swift Concepts
Scope
Swift follows the Object-Oriented programming principles for object
visibility.
Example
var global:Int = 1
class Scopetest:NSObject{
........
var classlevel:Int = 2
private var classlevel2:Int = 3
32
Chapter 3 Core Swift Concepts
func firstFunction() {
var first:Int = 4
print(global) //this will print the value 1
print(classlevel) //this will print the value 2
print(classlevel2) //this will print the value 3
print(first) //this will print the value 4
if(first < 1){
var second:Int = 5
}
print(second) - second is not visible as it is defined in the
conditional block
}
func secondFunction() {
var second:Int = 6
print(global) //this will print the value 1
print(classlevel) //this will print the value 2
print(classlevel2) //this will print the value 3
print(first) // error - first scope is restricted to
the firstFunction
print(second) //note this will print 6 not 5
}
}
class ScopeTest2:ScopeTest{
....
func printValues(){
print(global) //this will print the value 1 as this a program
level value
print(classlevel) //this will print the value 2 as it is a
class level variable and ScopeTest2 is an inherited class
print(classlevel2) // error – private variable can only be
accessed by the class
33
Chapter 3 Core Swift Concepts
Functions
The concept of modular programming highly encourages writing logical
code in self-contained blocks. This helps in not only getting the code more
modularized, hence readable, but also in case if the same block of code is
required for execution multiple times, we can define it once and use it by
just calling the function. A Swift function can be
• Simple block of code with no input or output
parameters. When the function is called, it executes the
code within its body.
34
Chapter 3 Core Swift Concepts
Example
Class BookViewController:UIViewController{ //Class
BookViewController is of type UIViewController System class
override func viewDidAppear(_ animated: Bool) { // notice
override, we are overriding the parent function viewDidAppear
// it is taking a bool value in a
variable called animated
addNumbers() // it will print "Number is 3"
addNumbers(firstNo: 20, secondNo: 30) // it will print
"Number is 50"
// notice it is mandatory to provide parameter
name when _ is not used
before the parameter name in defining the function
let finalNo = addNumbers(2, 30) // this function return 32
// notice the parameter name is not required
as the parameter has _ before the name
// notice finalNo type is not defined. Swift
automatically typecast to the required type
print("Number is \(finalNo)") // it will print 32
}
private func addNumbers(){ // function not taking any inputs
not returning any value
let firstNo:Int = 1
let secondNo:Int = 2
35
Chapter 3 Core Swift Concepts
36
Chapter 3 Core Swift Concepts
37
Chapter 3 Core Swift Concepts
38
Chapter 3 Core Swift Concepts
Type Casting
While writing Swift programs, we will have occasion when we have
downcast a variable from its superclass to a subclass. This could be both
for native data types (for example, converting an Int64 to Int, or a Double
to Int) or for custom data type. For custom data type, we will take an
example of our Book Type to show how it works
39
Chapter 3 Core Swift Concepts
class Book {
var name: String
init(name: String){
self.name = name
}
}
class Fiction:Book {
var genre: String
init(name:String, genre: String){
self.genre = genre
super.init(name: name)
}
}
40
Chapter 3 Core Swift Concepts
class Nonfiction:Book {
var genre:String
init(name:String, genre:String){
self.genre = genre
super.init(name: name)
}
}
41
Chapter 3 Core Swift Concepts
func getBookCounts(){
var fictionCount = 0
var nonFictionCount = 0
for book in library{
if book in Fiction{
fictionCount += 1
}else if book in Nonfiction{
nonFictionCount += 1
}
}
print("Total Fiction books are \(fictionCount) and the
Total Non Fiction books are \(nonFictionCount)")
}
Loop Controls
Swift provides many ways to loop through an array of objects. We will go
through the most important ones.
For-In Loops
For-in loops are used to iterate over items of an array.
42
Chapter 3 Core Swift Concepts
1
2
3
43
Chapter 3 Core Swift Concepts
While Loop
While loop is executed till a condition is met. In the following example,
we will print the books in the library count is equal to 2 (the count
starts from 0)
44
Chapter 3 Core Swift Concepts
func whileDemostration(){
var count = 0
while count < 2 {
print(library[count].name)
}
count += 1
}
UI Color
This chapter introduces the UI Color feature in Swift. The following
sections will help you to understand the UI Color Object in greater detail.
Overview
UI Color is an object that stores color data and sometimes opacity. UI
Color inherits from the NSObject, the root class for most Swift hierarchies.
UI Color helps to bring color to your App, allowing you to customize your
App's appearance, communicate with the user, and help them to visualize
your data. Make sure to read and understand section two, Guidelines for UI
Color, before implementing color into your App. UIColor includes many
45
Chapter 3 Core Swift Concepts
different class prosperities that create colors such as blue, green, and more.
You can create more unique shades by changing the values of the colors
through RGB, hue, and saturation.
46
Chapter 3 Core Swift Concepts
Fourth, important objects in your App should stand out. For example,
if you are writing an App that has a check bar, you must make sure that
the color you choose for the check bar stands out and is not hidden by the
other colors in your App. Don’t use the same color for interactive and non-
interactive elements to avoid any confusion from the user.
Fifth, it is always better to have alternate color options for the user.
Allow the user to choose which color he/she wants to use the App in by
making the option in settings or elsewhere in the application. Also create
two accent colors for each color option provided so that the App looks
good in both dark mode and light mode.
Keep these notes in mind when making your Swift App.
System Colors
When implementing colors into your App, it is best to use an API. Never
hard code system color values into your App as iOS offers a range of system
colors already in the system. Figure 3-2 provides a table of the UI Colors
in Swift
47
Chapter 3 Core Swift Concepts
EĂŵĞ ^ǁŝŌh/W/
ůƵĞ ƐLJƐƚĞŵůƵĞ
ƌŽǁŶ ƐLJƐƚĞŵƌŽǁŶ
LJĂŶ ƐLJƐƚĞŵLJĂŶ
'ƌĞĞŶ ^LJƐƚĞŵ'ƌĞĞŶ
/ŶĚŝŐŽ ƐLJƐƚĞŵ/ŶĚŝŐŽ
DŝŶƚ ƐLJƐƚĞŵDŝŶƚ
KƌĂŶŐĞ ^LJƐƚĞŵKƌĂŶŐĞ
WŝŶŬ ƐLJƐƚĞŵWŝŶŬ
WƵƌƉůĞ ƐLJƐƚĞŵWƵƌƉůĞ
ZĞĚ ƐLJƐƚĞŵZĞĚ
dĞĂů ƐLJƐƚĞŵdĞĂů
zĞůůŽǁ ƐLJƐƚĞŵzĞůůŽǁ
'ƌĂLJ dŚĞƌĞĂƌĞƐŝdž
ƐŚĂĚĞƐŽĨŐƌĂLJ
ƐLJƐƚĞŵ'ƌĂLJ
ƐLJƐƚĞŵ'ƌĂLJϮ
ƐLJƐƚĞŵ'ƌĂLJϯ
ƐLJƐƚĞŵ'ƌĂLJϰ
ƐLJƐƚĞŵ'ƌĂLJϱ
ƐLJƐƚĞŵ'ƌĂLJϲ
48
Chapter 3 Core Swift Concepts
Syntax
In the following example, we are setting the color of the View Controller
background color to white.
49
Chapter 3 Core Swift Concepts
CG Color
Swift allows access to the fundamental Core Graphics for layering,
transforming, or coloring. For instance, for an UI Button, we can access
its core layer and color the border with a defined color. Since it is the core
graphics, we need to use CG Color instead of UI Color. We can either use a
CGColor graphics Init or convert a UI Color to CGColor.
let bookLabelButton:UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints =
false
button.contentEdgeInsets = UIEdgeInsets(top: 5, left:
5, bottom: 0, right: 0)
button.contentHorizontalAlignment = .center
button.contentVerticalAlignment = .center
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = UIFont.
boldSystemFont(ofSize: 16)
button.clipsToBounds = true
button.titleLabel?.numberOfLines = 5
button.titleLabel?.lineBreakMode = NSLineBreakMode.
byWordWrapping
button.backgroundColor = UIColor(displayP3Red: 89/255,
green: 140/255, blue: 217/255, alpha: 1)
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.gray.cgColor
button.layer.cornerRadius = 5
button.layer.shadowPath = UIBezierPath(rect: button.
bounds).cgPath
button.layer.shadowRadius = 5
50
Chapter 3 Core Swift Concepts
button.layer.shadowOffset = .zero
button.layer.shadowOpacity = 1
return button
}()
Note that the border color is set in two ways:, one using system
color and the other using UIColor RGB. Also, notice the typecasting to
cg color.
UINavigationBar
UINavigationBar in most of the cases will be used as part of a Navigation
controller to navigate between screens (Figure 3-3). In such cases, the UI
Navigation creates a navigation item stack and uses system-defined push
and pop to navigate between the screens in the stack.
A UI Navigation Bar typically appears at the top of the screen and has
three parts, a display title, a left Bar Button Item, and a right Bar Button
Item. The buttons can be used to perform any action or function and
it behaves like a UI Button. The events and properties of UI Button are
inherited by the UI Bar Button Item.
51
Chapter 3 Core Swift Concepts
52
Chapter 3 Core Swift Concepts
53
Chapter 3 Core Swift Concepts
imageView.layer.cornerRadius = 5
imageView.layer.borderWidth = UIColor.blue.cgColor
when applied, it will create a rounded corner like the image that
follows:
(continued)
54
Chapter 3 Core Swift Concepts
Image Image View can scale the image based on its size. In the following
Size and code, we are creating a frame object and setting its height, width,
positioning X and Y coordinates, and assigning it to the frame property of our
imageView Object
var imageViewFrame = imageView.frame
imageViewFrame.size.width = 200
imageViewFrame.size.height = 200
imageViewFrame.origin.x = 0
imageViewFrame.origin.y = 0
imageView.frame = imageViewFrame
Please note this can be applied to any UI visual object in Swift.
Key Methods
UI Image View does not offer any default method; if we need to set up a
click event, we need to use the Add Gesture Recognizer. Please note, this
can be applied to any UI visual object in Swift.
55
Chapter 3 Core Swift Concepts
The first line defines a tap gesture, and it defines a method imageTapped which will
be called when the image will be tapped
The second line ensures user can interact with the imageView object
And the third line adds the gesture to the imageView object
In the custom defined method imageTapped, we can define the action we want to
perform.
UITextField
Text Field is one of the most common UI Objects we will be using while
creating iOS Apps. We use UITextField to gather inputs from users. Users
can enter values using the onscreen keyboard, which is configurable to
display the right keys for easier data inputs (example, the display keyboard
can be a numeric pad to restrict users to only enter digits for a phone
number text field). In our App, we will use Text Field to input values such
as Book Name, Author Name, and Search String.
56
Chapter 3 Core Swift Concepts
57
Chapter 3 Core Swift Concepts
Key Methods
There are three events that come very handy in dealing with UI Text Field.
These events can be attached to the text field using the addTarget Method.
The methods are explained as follows:
This event is called as soon as the user starts typing in the Text Field.
Operations such as setting the border color before user types is a good
example of operations we can do in this event.
bookTextField.addTarget(self, action:
#selector(bookTextFieldDidEnd), for: .editingDidBegin)
This event is called when focus moves away from the Text Field.
Operations such as resetting variables or capturing entered data are good
examples of operations we can do in this event.
58
Chapter 3 Core Swift Concepts
This event is called every time a user starts typing. Operations such as
checking the entered value or formatting input fields based on data entry
(like phone formatting), are a good example of operations you can do in
this event.
The textfield variable will give access to the current text filed.
59
Chapter 3 Core Swift Concepts
UIAlertController
Alert Dialogs are an important part of iOS SDK, which, based on user
interaction with the App, we can display information and prompt users to
take quick and appropriate action. Alerts come in very handy to confirm
user actions to ensure it was not done accidently. Example, a record
getting a confirmation from a user delete action is invoked. In this section,
we learn how to create an alert, display it and, based on user response, take
appropriate action.
Defining an Alert
AlertViewController has a constructor which takes the following
arguments:
60
Chapter 3 Core Swift Concepts
Figure 3-4 shows how the system will show the alert screen.
61
Chapter 3 Core Swift Concepts
alertController.addAction(okAction)
alertController.addAction(cancelAction)
Alert Display
So far, we have defined the Alert Controller object, defined the action
buttons, and added it to the Alert Controller. Now we are ready to display
the alert. Use the following code to achieve the same:
self refers to the View Controller and we are using the current view’s
present method to display the alert.
UITableView
Table Views are one of the most important UI Objects in the iOS toolkit. A
table view displays a collection of vertical rows where each row contains
a collection of information that is displayed on the screen. The table view
is scrollable, allowing users to scroll through a long list of content. Table
view also allows grouping of content and shows the content in separate
sections. Each displayed row is a table view cell, which can be customized
to display any UI Object. In this section, we will learn to create tables using
code. We can also create a table view using the StoryBoard.
62
Chapter 3 Core Swift Concepts
tableView.isScrollEnabled = true
return tableView
}()
The preceding line of code will create a table view with a white
background color and scroll enabled to allow the user to scroll up or down
the record set.
Delegate Methods
To implement a table-view, our class needs to inherit two delegates as
explained in the following:
63
Chapter 3 Core Swift Concepts
tableView.delegate = self
tableView.dataSource = self
• Register the table with the type of Table Cell and give
it a reference name. Cells are used to display data in
the table. The default system-defined table cell name
is UITableViewCell. UITableViewCell has two labels to
display text. A main data label and a detailed text label.
We can also customize the cell to display any UI Object
component.
tableView.register(MotivationTableViewCell.self,
forCellReuseIdentifier: "all")
64
Chapter 3 Core Swift Concepts
view.addSubview(tableView)
commonFunctions.setFieldLayout(mainField: tableView,
constraintField: view!, topAnchor: 60, leftAnchor: 0,
rightAnchor: 0, heightAnchor: 460)
Displaying a Table
We will discuss three system-defined functions which will help us to show
the table and relevant data on the screen.
• Number of sections
• Number of Rows
65
Chapter 3 Core Swift Concepts
Selection of Row
If we need to perform an action upon row selection, the following code will
come in handy. We can perform function like displaying another table or
another view.
66
Chapter 3 Core Swift Concepts
Table Header
Every section of the table can be assigned a Header view. Header view can
be customized to show any UI Object. In the following example, we will
customize the header view to show a UI label object.
67
Chapter 3 Core Swift Concepts
Cell Heights
Cell and header view heights are by default set to 40 pixels. When we
define custom cell and views, we may have to set the height of each cell or
view accordingly.
Swiping Function
Table View cell also offers a swipe gesture where we can add other
executable functions. The following code creates a delete swipe action
gesture.
68
Chapter 3 Core Swift Concepts
deleteAction.backgroundColor = .red
deleteAction.image = UIImage(systemName: "trash")
return UISwipeActionsConfiguration(actions:
[deleteAction])
}
Summary
This is the only chapter in the book which introduces the user to core
concepts of coding using the Swift language. This chapter is not exhaustive
by any means, but provides the most important concepts. We learned the
following in the chapter:
• Typecasting of Objects
Now that we have learned the core concepts, we will apply them in the
subsequent chapters to create our Book Tracker App.
69
CHAPTER 4
Book Tracker
Basic App Building
The next five chapters are dedicated to creating the Basic version of
the Book Tracker App. We will use the concepts we have learned in the
previous chapter to create our App. In this chapter, we will create the Tab
View Controller for our basic version of the book tracker Application. The
Tab View Controller will help us navigate between three main screens -
Main Display, Adding a Book, and Searching Book.
Let us first set up a Tab View Controller. A Tab view controller is a built-
in navigator that helps a user to move between tabs.
Follow the following steps to create an application:
72
Chapter 4 Book Tracker Basic App Building
• Language is Swift
73
Chapter 4 Book Tracker Basic App Building
74
Chapter 4 Book Tracker Basic App Building
75
Chapter 4 Book Tracker Basic App Building
76
Chapter 4 Book Tracker Basic App Building
77
Chapter 4 Book Tracker Basic App Building
78
Chapter 4 Book Tracker Basic App Building
79
Chapter 4 Book Tracker Basic App Building
Figure 4-10. Updated Visual Builder with the three View Controller
80
Chapter 4 Book Tracker Basic App Building
81
Chapter 4 Book Tracker Basic App Building
• Title as “Display”
82
Chapter 4 Book Tracker Basic App Building
Let us now run the application. For running, you can either run it on
your phone by connecting the phone to the laptop or on a simulator. In
this example, we will run it on an iPhone 12 Simulator. You can select the
simulator from the top bar, as shown in Figure 4-15.
83
Chapter 4 Book Tracker Basic App Building
84
Chapter 4 Book Tracker Basic App Building
Summary
In this chapter, we created the Tab View controller of our Basic version
of the Book Tracker App. The Tab view controller will have three tabs:
displaying Books, searching existing books, and an option to add a Book to
the repository.
In the next chapter, we will learn about Adding a Book to our Book
Tracker App.
85
CHAPTER 5
• Book Name
• Author Name
• Book Description
88
Chapter 5 Adding Book Screen
89
Chapter 5 Adding Book Screen
90
Chapter 5 Adding Book Screen
91
Chapter 5 Adding Book Screen
92
Chapter 5 Adding Book Screen
93
Chapter 5 Adding Book Screen
94
Chapter 5 Adding Book Screen
95
Chapter 5 Adding Book Screen
96
Chapter 5 Adding Book Screen
97
Chapter 5 Adding Book Screen
98
Chapter 5 Adding Book Screen
Add the code shown in Table 5-4 to the class, so the visibility is across the
class. These are UI objects that we will define programmatically.
99
Chapter 5 Adding Book Screen
This will define the UI Navigation Bar object and assign it to a variable
navigationBar.
navigationBar.translatesAutoresizingMaskIntoConstraints =
false
This will set the background color of the bar. Please refer to the UI Color
reference section to learn how to set color.
We are creating a UINavigationItem Object which can hold a left and right action
button. The constructor takes the title to be displayed on the Navigation Bar.
The next section will create two navigation items and set the items on the
Navigation Bar.
100
Chapter 5 Adding Book Screen
We are defining an UI Image for the Reset button. iOS provides a list of system-
defined icons. We are using the icon arrowshape.turn.up.left.fill and applying the
symbol configurations for size and color.
We are now assigning the image we created on a UI Bar Button. If a user clicks
on it, it will call the function resetButtonAction ( The system event is Touch Up
Inside). The following three lines are now repeated for the save button.
l et saveSymbolConfiguration = UIImage.
SymbolConfiguration(pointSize: 20, weight: .black)
l et saveImage = UIImage(systemName: "s.circle.fill",
withConfiguration: saveSymbolConfiguration)
l et saveItem = UIBarButtonItem(image: saveImage,
landscapeImagePhone: saveImage, style: .plain, target:
nil, action: #selector(SaveButtonAction))
navItem.rightBarButtonItem = saveItem
The saveItem we defined before is assigned to the right Bar Button Item of the
navigation Item.
navItem.leftBarButtonItem = resetItem
(continued)
101
Chapter 5 Adding Book Screen
The reset button and Save button functions defined as part of the
navigation bar item are described in Table 5-5. All user-defined functions
precede with @objc followed by the system-defined func for functions.
102
Chapter 5 Adding Book Screen
Next, we are going to define the Text Field to allow users to enter book-
related information. The code is defined in Table 5-7.
103
Chapter 5 Adding Book Screen
we are defining a textfield variable. All the properties will be set to the textField
variable, and toward the end of the definition, we will return the bookTextField
variable for display on the view controller.
3. t extField.translatesAutoresizingMaskIntoConstraints =
false
this is to set the background color of the Text field to Gray and border style to
rounded Rectangle
5.
textField.attributedPlaceholder =
NSAttributedString(string: "Enter Book Name", attributes:
[NSAttributedString.Key.foregroundColor: UIColor.white])
(continued)
104
Chapter 5 Adding Book Screen
6. textField.textColor = .white
7. return textField
}()
The textField is returned, the curly bracket is closed, and function is completed [()]
105
Chapter 5 Adding Book Screen
This section is the same as the previous, where we have defined the author, genre,
and status text fields.
106
Chapter 5 Adding Book Screen
To draw the UI Objects, we need to set the object Anchors. Since this
will be done multiple times, we will define it once in a custom class called
CommonFunctions.
As shown in Figure 5-10, create a Cocoa Touch Class called
CommonFunctions of subclass type NSObject.
We will now add a function which can help anchor the UI Objects on
the screen. The function is public and is at class level. The details are in
Table 5-8.
107
Chapter 5 Adding Book Screen
108
Chapter 5 Adding Book Screen
Notice that input variables mainField and ConstraintField is of type Any. This is
because we can send any UI Object to the function which can then be typecast to
the respective UI object types.
(continued)
109
Chapter 5 Adding Book Screen
110
Chapter 5 Adding Book Screen
We are now going to write a custom function which will draw the
screen for us. We will name the function as drawInputScreen(). This will
be a class level function and will be called when the class will load first
(ViewWillAppear – Please refer to Life Cycle Methods to understand the
lifecycle function hierarchy). The code details are in Table 5-10.
111
Chapter 5 Adding Book Screen
Every ViewController has a default UI view on which the UI Objects are layered.
view.addSubview(addNavigationBar)
commonFunctions.setFieldLayout(mainField: addNavigationBar,
constraintField: view!, topAnchor: 60, leftAnchor: 0,
rightAnchor: 0, heightAnchor: 40)
view.addSubview(bookTextField)
commonFunctions.setFieldLayout(mainField: bookTextField,
constraintField: addNavigationBar, topAnchor: 60, leftAnchor:
5, rightAnchor: -5, heightAnchor: 40)
The preceding two statements add the book Input Text Field below the
navigation bar. Notice the constraint field this time is the navigation bar, and it is
separated by 60 pixels. We are leaving 5 pixels from the left and the right.
The Author, Genre, and Text Field are added to the screen in a similar way.
Please note how the constraint field is changing for each one of them. Also
note, the right and left anchor are set to zero because we want all fields to be
of the same size as Book Text field, as these fields are taking Book Text Field as
constraint.
112
Chapter 5 Adding Book Screen
The code defined in Table 5-11 is the lifecycle method, which will
execute first as the Add Book screen opens.
113
Chapter 5 Adding Book Screen
114
Chapter 5 Adding Book Screen
4. And put the focus back on the first empty text field,
forcing the user to enter value.
115
Chapter 5 Adding Book Screen
3. if(bookTextField.text == ""){
bookTextField.placeholder = "Book Name cannot be blank"
bookTextField.layer.borderWidth = 2
bookTextField.layer.cornerRadius = 5
bookTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
4. }else{
bookTextField.layer.borderWidth = 0
5. }
6. if(authorTextField.text == "")
7. {
authorTextField.placeholder = "Author Name cannot be
blank"
authorTextField.layer.borderWidth = 2
authorTextField.layer.cornerRadius = 5
authorTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
8. }else{
authorTextField.layer.borderWidth = 0
9. }
(continued)
116
Chapter 5 Adding Book Screen
11. }else{
genreTextField.layer.borderWidth = 0
12. }
14. }else{
statusTextField.layer.borderWidth = 0
15. }
16. if(canSave){
saveBook()
17. }
18. }
(continued)
117
Chapter 5 Adding Book Screen
This is the function which gets called when the user clicks on the save button.
2. var canSave:Bool = true
This is a function level variable which is set to false if any of the field is left
blank
3. if(bookTextField.text == ""){
This statement compares if the book text field is blank. If it is blank, the
following five statements are executed
bookTextField.layer.borderWidth = 2
We are setting the Text field border to size 2 to make it promptly visible
bookTextField.layer.cornerRadius = 5
The text field corner radius will give the rectangular text box a nice, rounded
curve
bookTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
Setting the canSave Boolean variable to false, flagging we cannot save the
record yet
(continued)
118
Chapter 5 Adding Book Screen
4. }else{
If enclosure, and the else part of the if (when the text field is not blank)
bookTextField.layer.borderWidth = 0
setting the border width to zero so that it returns to the original status
5. }
6. if(authorTextField.text == ""){
authorTextField.placeholder = "Author Name cannot be
blank"
authorTextField.layer.borderWidth = 2
authorTextField.layer.cornerRadius = 5
authorTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
7. }else{
authorTextField.layer.borderWidth = 0
8. }
(continued)
119
Chapter 5 Adding Book Screen
9. if(genreTextField.text == ""){
genreTextField.placeholder = "Genre cannot be blank"
genreTextField.layer.borderWidth = 2
genreTextField.layer.cornerRadius = 5
genreTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
10. }else{
genreTextField.layer.borderWidth = 0
11. }
13. }else{
statusTextField.layer.borderWidth = 0
14. }
15. if(canSave){
If canSave is set to true, it means all fields have the needed values and we
can save the book
16. saveBook()
Functions to save the book
17. }
}
120
Chapter 5 Adding Book Screen
Run the program. You will see screens like how it is shown in
Figure 5-12.
:KHQ DOO ILHOGVDUH :KHQ %RRN QDPH :KHQ $XWKRU LV :KHQ *HQUH LV :KHQDOO ILHOGV
EODQN WKH UHG LVHQWHUHG DOO RWKHU HQWHUHGDOO RWKHU HQWHUHGDOO RWKHU KDYH YDOXH
ERUGHU LV RQ DOO ILHOGV KDYH UHG ILHOGV KDYH UHG ILHOGV KDYH UHG
ILHOGV ZLWKWKH ERUGHUV ZLWK ERUGHUV ZLWK ERUGHUV ZLWK
ZDUQLQJ ZDUQLQJ ZDUQLQJ ZDUQLQJ
SODFHKROGHU WH[W SODFHKROGHU WH[W SODFHKROGHU WH[W SODFHKROGHU WH[W
121
Chapter 5 Adding Book Screen
In the current validation, the red border doesn’t go away even after
providing a value until the user clicks on the save button. Aesthetically, it
doesn’t appear right. The code in Table 5-13 helps to solve that problem.
The steps are as follows:
•
#selector allows defining a custom function for the action. In our case, it
is textFieldEditingDidBegin. We need to define this function to achieve the
desired behavior.
This is our custom method and is called when the user starts typing on any of the
text fields. The sender has the reference to the Text Field, and we can use it to set
the border width to zero.
122
Chapter 5 Adding Book Screen
123
Chapter 5 Adding Book Screen
Define Group
In the DatabaseFunctions class define a dispatch group and a
Queue. We will need this because all calls to the iCloud CRUD functions
(create, query, update, and delete) are asynchronous. So, when we call
these functions, we would be notified when the job is done (as iCloud is
a repository located outside of your App). The Dispatch group helps us
to handle the notification and the queue will be defined to know where it
should be notified when the work is done. Define this at the class level. So,
all functions can use it. See the code in Table 5-16 for details.
124
Chapter 5 Adding Book Screen
125
Chapter 5 Adding Book Screen
1. func saveBook(book:Book){
This is a custom function to save the Book data entered by the user. It takes an
object of type Book class.
Row 2 to 5 is getting access to the custom Shared Zone of the Cloud Database
for the user (Private Database). Please note, a custom zone is required for record
sharing with other users, which we will learn later. By default, iCloud creates a
default zone, which cannot be used for sharing records.
2.
let ckRecordZoneID = CKRecordZone(zoneName:
"_SharedZone")
We are defining the zone ID where we are going to store our book record.
SharedZone is a custom name.
3. l et privateDatabase =
CKContainer.default().privateCloudDatabase
4. l et ckRecordID = CKRecord.ID(zoneID:
ckRecordZoneID.zoneID)
We are now defining a Record ID to hold our book pointing to the Shared Zone
5.
let aRecord = CKRecord(recordType: "Book",
recordID: ckRecordID)
The record ID is now pointing to Book Record Type, which, if you recall, we
created during iCloud setup. We will use this record to store our book details.
(continued)
126
Chapter 5 Adding Book Screen
The function is called with a book object. We are now assigning the book object
book name value to the record with the field “bookname”. Please note, the name
of the field should exactly match to the field name we provided while creating the
book. Steps 7, 8, and 9 is a repeat of Step 6 for other book fields.
7. aRecord["authorname"] = book.authorname
8. aRecord["genre"] = book.genre
9. aRecord["status"] = book.status
10.
privateDatabase.save(aRecord, completionHandler: {
(record, error) -> Void in
recall we have the variable privateDatabase pointing to the User Private Database
in iCloud. We are calling the built-in save method to save our record. Save
takes the CKRecord Type (which in our case is Book type) parameter and it has
a completion handler. Please note, all iCloud functions are asynchronous. So,
when the database is done with the work, it returns either a record it saved (if
successful) or an error message. The parameter record and error are to capture
the same.
11. DispatchQueue.main.async {
On the main thread of the Dispatch Queue, the async routine will invoke when
the work is done.
127
Chapter 5 Adding Book Screen
13. print(error)
14. }
15. else{
16. self.syncQueue.async {
or else, we will notify the waiting process (from where our custom saveBook()
will be called)
17. self.group.leave()
group leave will notify the waiting function (from where our custom saveBook()
will be called)
18. }
19. }
20. }
21. })
22. }
128
Chapter 5 Adding Book Screen
2. book.bookname = bookTextField.text!
setting the book object book name property to the book Text Field value
3. book.authorname = authorTextField.text!
setting the book object Author Name property to the Author Text Field value
4. book.genre = genreTextField.text!
setting the book object Genre property to the Genre Text Field value
5. book.status = statusTextField.text!
setting the book object Status property to the Status Text Field value
6. saveBook()
7. }
fileprivate before a func makes it only accessible in the defined file. Please note,
this is the local saveBook() function to the file AddBookViewController.swift
2. databaseFunctions.group.enter()
we are accessing the group variable from the DatabaseFunctions file using the
reference variable databaseFunctions. enter() allows us to enter the wait loop. It
will wait until it is notified.
3. databaseFunctions.saveBook(book:book)
After entering the wait, we are now calling the DataBaseFunctions saveBook
method. Notice it takes Book as a variable. Also note, we have stored the class
instance book with all required values when the save button was clicked.
4. databaseFunctions.group.notify(queue: .main) {
When iCloud returns to the main queue, it will notify the group object. At this
time, we can do any post-saving activities.
5. print("Record Saved")
7. }
130
Chapter 5 Adding Book Screen
Post Save
UIAlertController
Alerts are a quick way to display contextual information based on certain actions
that a user performs. Alert can also show action buttons and, based on what user
chooses, a certain function can be executed.
When you run the program now and save a book, the screen shows all
the last entered book data, except printing “Record Saved” on the console.
After saving, we would do the following:
We are adding code after the book is saved. The highlighted portion in
Table 5-21 is what we replaced from the book save function.
131
Chapter 5 Adding Book Screen
132
Chapter 5 Adding Book Screen
3.
let okAction = UIAlertAction(title: "OK", style:
UIAlertAction.Style.default) {
We need to create an action button on the alert window – this is the OK Button.
We will only have one button for this alert, as this is information-only alert
window.
4. UIAlertAction in
This is inline code. If we need to take any action, we will take it here. For our
window, we are not taking any action, so it is blank.
5. }
7. alertController.addAction(okAction)
9.
self.present(alertController, animated: true,
completion: nil)
10. self.resetField()
This is a custom function to reset the field value (Defined in the next section)
133
Chapter 5 Adding Book Screen
Run the program. You will see a screen, as shown in Figure 5-13, after
the book is saved.
Reset Field
When the user clicks the reset button on the navigation bar, all data is
removed, and the text fields show the placeholder texts. Consider this
when you want to clear the fields without manually going to every field to
delete the words. Table 5-22 has the relevant code.
134
Chapter 5 Adding Book Screen
resetButton function is defined for the navigation bar left button. This is the
implementation of the same
2. resetField()
func resetField(){
bookTextField.text = ""
authorTextField.text = ""
genreTextField.text = ""
statusTextField.text = ""
bookTextField.becomeFirstResponder()
}
1. func resetField(){
Custom function definition
2. bookTextField.text = ""
3. authorTextField.text = ""
4. genreTextField.text = ""
135
Chapter 5 Adding Book Screen
5. statusTextField.text = ""
6. bookTextField.becomeFirstResponder()
Making the Book Name Field the first Responder, which will bring the cursor to
the Book Name Text Field and bring up the keyboard.
7. }
Summary
This chapter introduced many core concepts which will be your learning
bedrock for building iOS App. We learned about Tab View Screen
allocation and transition, input validation and user interactions. We also
got introduced to CloudKit for storing our book record Asynchronously to
iCloud repository. After adding the necessary code for Adding a Book to
our Book Tracker repository, we learned how to test our application using
iOS simulator. iOS simulator is a great way to test form response on various
models of iPhone.
Now that we have books in our Book Tracker database, next we will
learn about retrieving them from iCloud and displaying them on our App.
136
CHAPTER 6
138
Chapter 6 Displaying the Book Records
139
Chapter 6 Displaying the Book Records
140
Chapter 6 Displaying the Book Records
141
Chapter 6 Displaying the Book Records
142
Chapter 6 Displaying the Book Records
Like how we Saved the Book, all CRUD functions we will write in one
file (DatabaseFunctions) – this increases modularity and confirms the
Object-Oriented Programming.
Write the piece of code as described in Table 6-1 to the
DatabaseFunctions.
func bookQuery() {
let pred = NSPredicate(value: true)
let query = CKQuery(recordType: "Book," predicate: pred)
let operation = CKQueryOperation(query: query)
CKContainer.default().privateCloudDatabase.add(operation)
let ckRecordZoneID = CKRecordZone(zoneName: "SharedZone")
operation.zoneID = ckRecordZoneID.zoneID
operation.recordFetchedBlock = { record in
let book = Book()
book.bookname = record["bookname"]
book.authorname = record["authorname"]
book.genre = record["genre"]
book.status = record["status"]
self.books.append(book)
}
(continued)
143
Chapter 6 Displaying the Book Records
Remarks to create a MarkUp block for the Book Query code. This is for better
readability.
2. func bookQuery() {
This is the name of the functions that will hold book query functionalities.
3. let pred = NSPredicate(value: true)
144
Chapter 6 Displaying the Book Records
The operation we have set with the query is now added to the user private cloud
database where the user schema exists. Currently, the query is sent asynchronously
to the iCloud for execution. Please note the following statement will only be
executed when the query returns to this thread. This is the reason why when we call
the bookQuery() function we will put it in a wait loop.
7. operation.recordFetchedBlock = { record in
When iCloud returns, it will first land on the record fetched block. Notice the word
record which holds the individual book record (as per the Book table defined in
iCloud Data Dictionary). As we loop through the fetched records, we can store it in
a local Book (Book is a custom class defined in DatabaseFunctions class, which
mimics the Book Table defined in the iCloud database) array for display purposes.
9. book.bookname = record["bookname"]
145
Chapter 6 Displaying the Book Records
13. self.books.append(book)
Now that we have the book information in the local variable book, we will add this
to our class level books array. The loop will continue until all books are retrieved and
stored.
14. }
15.
operation.queryCompletionBlock = { [unowned self] (cursor,
error) in
when the previous fetch is complete, the query completion block is called. Notice
you get the cursor to the record set (we will not use it in our code) and if there are
any errors it will be stored in the error variable.
16. DispatchQueue.main.async {
This statement stores the error returned from the asynchronous iCloud function into
a variable named error.
18. print(error)
19. }else {
20. self.syncQueue.async {
We have defined a custom queue called syncQueue, on which we will wait for the
asynchronous function to return.
(continued)
146
Chapter 6 Displaying the Book Records
23. }
24. }
25. }
147
Chapter 6 Displaying the Book Records
2. databaseFunctions.group.enter()
148
Chapter 6 Displaying the Book Records
Now we will call the bookQuery functions from DatabaseFunction (we wrote in the
previous section), which will query the CloudKit database schema.
4. databaseFunctions.group.notify(queue: .main) {
this is the wait part of the DispatchGroup, when the child thread notifies the main
thread, the code in this block will be executed.
5. self.drawBookDisplay()
This is the function to display the table view, which we will discuss in the next
section.
6. }
7. }
149
Chapter 6 Displaying the Book Records
Now that we have queried the Cloudkit table, next we need to display the
value on the screen. Swift has a UIObject called Table View which we will
use for this purpose. Table View is one of the most important UI Objects
that we need to learn to create iPhone Apps. The default Weather, Stock
Ticker Apps are customized rendition of Table View. In the Core concepts
section, we will dedicate a section on Table View, its related functions,
and how to customize a table to create aesthetically aligned professional
applications. For now, we will learn the basic table view setup to display all
books on a one column table.
Before we start, let us understand a few important aspects of
table view.
Let us now implement our Book table view to display the retrieved books.
150
Chapter 6 Displaying the Book Records
Table View is created and stored in a variable called tableView. We will use
tableView variable to set some initial properties of the table
3.
tableView.translatesAutoresizingMaskIntoConstraints =
false
4. tableView.backgroundColor = .white
Setting the table view background color to white. Note, we can set a custom color
using the UIColor constructor
(continued)
151
Chapter 6 Displaying the Book Records
We want the table to scroll to display all books as user scrolls up or down
6. return tableView
we are returning the tableView, so, when we use the bookTableView variable, all the
settings we have set will be available
7. }()
We need to extend Table View Delegate and Data Source. This is required to
enable all Table View functions. Please note, the moment you add the delegates,
you will get an error stating, DisplayViewController does not conform to protocol
“UiTableViewDataSource”. We need to add a minimum of two methods as
explained in steps 5 and 6.
152
Chapter 6 Displaying the Book Records
Table view displays values in Table View Cell, we need to also define the
TableView Cell (Note, Swift provides a default Table View Cell, which we will
use for now. Later we will learn about creating a custom Table View Cell for
custom table view appearances). Table 6-6 has the relevant code details.
2. func setup(){
3. bookTableView.delegate = self
setting the bookTableView Delegate to the class so that we can perform all Table
View functions
4. bookTableView.dataSource = self
setting the bookTableView Data source to the class so that we can perform all
Table View functions
5.
bookTableView.register(UITableViewCell.self,
forCellReuseIdentifier: "book")
Registering the table view to a default Cell Type and giving it an identifier name
“book”. We will use this identifier to display table value in the cell
6. }
153
Chapter 6 Displaying the Book Records
Since we are drawing UI Objects using code, our next step is to draw
the screen with the table. Table 6-8 has the relevant code details for
drawing the Book Display screen.
2. func drawBookDisplay(){
3. view.addSubview(bookTableView)
adding the bookTableView to the main View
(continued)
154
Chapter 6 Displaying the Book Records
5. }
this is built-in function signature which returns an integer value, which refers to the
total number of rows in a table
2. return self.databaseFunctions.books.count
Recall from our Query Book functions, after the query, the result is persisted in the
books variable in the databaseFunctions file.
3. }
155
Chapter 6 Displaying the Book Records
This is a built-in function signature which returns an UI table View Cell to display
custom value (in our case book name). This will iterate for all visible rows
2. l et cell = tableView.dequeueReusableCell(withIdentifier:
"book", for: indexPath)
156
Chapter 6 Displaying the Book Records
The system-defined cell reference has a default UI Textlabel. We are setting that
with current book name. Notice the array reference indexPath.row – this will give
the current row number of the Book Array, iterating from the first row until the last.
4. return cell
5. }
157
Chapter 6 Displaying the Book Records
158
Chapter 6 Displaying the Book Records
We will learn in Part II about customizing table views. Table View Cell
by default allows us to add an additional detailed text label. In this section,
we will learn about the same.
In our example, we will show the author’s name as detailed text next to
the Book name. Replace the cellforRowAt code with the code in Table 6-11.
159
Chapter 6 Displaying the Book Records
This is the default function which draws each cell of the table (We have learned this
before)
2.
//let cell = tableView.dequeueReusableCell(withIdentifier:
"book", for: indexPath)
3.
let cell = UITableViewCell(style: UITableViewCell.
CellStyle.value1, reuseIdentifier: "book")
We are calling the built-in table function UITableViewCell. There are many styles
available which will provide the layout of the cell. We use value1, and mentioned the
identifier which we have defined during table creation
4.
cell.textLabel?.text = self.databaseFunctions.
books[indexPath.row].bookname
160
Chapter 6 Displaying the Book Records
This is a new addition which will display the detailed Text, which in our case is the
author’s name.
6. return cell
And like before, we are returning the cell to draw on the table view display.
7. }
When we run our application, we now see the author’s name as shown in Figure 6-8.
161
Chapter 6 Displaying the Book Records
Note Play around with the style and see how the cell layout
changes.
1.
func tableView(_ tableView: UITableView,
titleForHeaderInSection section: Int) -> String? {
This is built-in table view function. Notice it returns a string, which is the Title for the
Table
3. }
(continued)
162
Chapter 6 Displaying the Book Records
Run the program and you will see the Title MyBooks on the top, as shown in
Figure 6-9. In Section II, we will learn how to beautify the table’s Header.
163
Chapter 6 Displaying the Book Records
Summary
Another very important chapter, where we learned about core data display
functions. Table Views are the most convenient way to display data on an
iOS App. We learned about the Table View setup, core table view functions,
manipulating basic display labels in table view, and working with Table
Header. We also learned additional cloudkit functions to retrieve book
records asynchronously from the iCloud repository.
In the next chapter, we will learn how to delete a book record from the
repository.
164
CHAPTER 7
Deleting a Table
Record
We now have a fully functional table view. We can add a book and we can
display all books with their respective author’s name. In this chapter, we
will learn how to delete a book record. From a user interface perspective,
we will add the delete button to the table cell as a swipe gesture. So, when
the user swipes left it will show the delete option and clicking the button
will allow the user to delete the record.
deleteAction.backgroundColor = .red
deleteAction.image = UIImage(systemName: "trash")
return UISwipeActionsConfiguration(actions:
[deleteAction])
}
This is a built-in function which implements the UI Gesture Swipe on the table cell.
Notice it requires UISwipeActionsConfiguration to be returned and provide reference
to the concerned table view and the cell details through the IndexPath variable.
(continued)
166
Chapter 7 Deleting a Table Record
3. })
4. deleteAction.backgroundColor = .red
For the action display, we can either set a label or an image. In our code, we are
setting this to the system trash icon
Now that all references are done, we are returning the deleteAction as a
UISwipeActionsConfiguration. Notice, the action is a collection of arrays. We can
define multiple swipe actions and return it for display.
7. }
(continued)
167
Chapter 7 Deleting a Table Record
Swiping Left will give the delete button. But clicking will not do anything as our
completion handler is empty. Next, we will write the code for delete. Figure 7-1
shows the simulator screen for Trailing left delete function.
168
Chapter 7 Deleting a Table Record
CKContainer.default().privateCloudDatabase.add(
operation)
operation.recordFetchedBlock = { record in
taskRecords.append(record.recordID)
}
(continued)
169
Chapter 7 Deleting a Table Record
m odifyRecordsOperation.modifyRecords
CompletionBlock = { records, recordIDs, error in
DispatchQueue.main.async {
if let error = error {
print(error)
}else{
self.syncQueue.async {
self.deleteBookNotes(bookname:
bookname)
}
}
}}
CKContainer.default().privateCloudDatabase.
add(modifyRecordsOperation)
}}
}
(continued)
170
Chapter 7 Deleting a Table Record
A custom-defined function which takes two input parameters: book name and the
author’s name.
taskRecords variable will store all record IDs that are returned by the query for
deletion. Note, the Cloudkit identifies every record with a unique record ID.
We are defining the condition for the query. Please make sure the field name is
matching to what we have in the database.
We are preparing the query on the “Book” Type with the predicate we have defined
before.
6. CKContainer.default().privateCloudDatabase.add(operation)
And we execute it on the user’s private cloud database. Note, once this function
is executed, we will be in wait mode (we get into wait mode when we call the
deleteBook function).
7. operation.recordFetchedBlock = { record in
Once the Query is executed, it returns to the record Fetched Block, which is a
predefined function of CKOperations.
(continued)
171
Chapter 7 Deleting a Table Record
We will loop through the result sets and add all record Ids to the custom defined
taskRecords. Note, record is a variable of CK Record Type. As we loop through it,
the record contains the record that matches the condition set forth in the predicate
of the query.
9. }
when the query is completed, it will come to the queryCompletionBlock. Note, this is
asynchronous. Cursor has all references to record, and if the query is unsuccessful,
the error variable will contain the failure reasons.
11. DispatchQueue.main.async {
13. CKContainer.default().privateCloudDatabase.
add(modifyRecordsOperation)
172
Chapter 7 Deleting a Table Record
14. modifyRecordsOperation.modifyRecordsCompletionBlock = {
records, recordIDs, error in DispatchQueue.main.async {
If there is an error in execution, this part will be executed. Notice error is an input
variable.
16. print(error)
17. }else{
If not, we have successfully deleted the record. The reference of the same, if
needed, are in records and Record IDs variable.
18. self.syncQueue.async {
19. self.group.leave()
and leave the group. You will see where the control goes from here in the next
section.
20. }
21. }
22. }}
23. }}
24. }
173
Chapter 7 Deleting a Table Record
Summary
In the last three chapters, we learned about the CRUD functions to
manipulate iCloud databases. In this chapter, we learned about deleting
a book record. From a user display standpoint, we also learned a swipe
function to display user-friendly actions.
The next chapter will conclude Part I of our learning. The last chapter
in Part I will focus on how to do conditional search and retrieve only those
books that the user wants to see. We will also learn about dynamic search
functions using Text Field built-in Edit functions.
174
CHAPTER 8
Searching Data
Screen
This chapter will complete the basic structure of our App. Search is the
third and last tab on our screen. Users will be able to search a book by its
title or will be able to get all books searched by an author name. Also, the
search will be dynamic, as the user types the letter it will show the book
name displayed.
textField.attributedPlaceholder =
NSAttributedString(string: "Search Terms", attributes:
[NSAttributedString.Key.foregroundColor: UIColor.gray])
textField.textColor = .black
return textField
}()
let searchTableView:UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints =
false
tableView.backgroundColor = UIColor.init(red: 255/255,
green: 253/255, blue: 208/255, alpha: 1)
tableView.layer.cornerRadius = 5
tableView.layer.borderWidth = 1
tableView.layer.borderColor = UIColor.black.cgColor
tableView.isScrollEnabled = false
return tableView
}()
176
Chapter 8 Searching Data Screen
2. searchTableView.delegate = self
3. searchTableView.dataSource = self
4. searchTableView.register(UITableViewCell.self,
forCellReuseIdentifier: "searchcell")
5. searchTableView.backgroundColor = UIColor.init(red: 202/255,
green: 202/255, blue: 202/255, alpha: 1)
Lines 2-4 we have explained before. If you need to learn more, please refer to
previous section.
(continued)
177
Chapter 8 Searching Data Screen
We need to set the delegate of the UI Text Field for search to the view controller.
Please note we need to include the UITextFieldDelegate in the class definition
For the Search Text Field, we need to add three built-in functions for drawing the
dynamic table population behavior. We can add a function to a UI Object using
addTarget method. It takes the target object (which is self), the name of the
function as #selctor (a custom name is given which we need to define later. In this
statement, the custom method name is textFieldEditingChanged), and the control
event, which is the system-defined name editingChanged is called when any
change happens in the text field. Every time the user types, this method is called.
editingDidBegin is a system-defined function that is called when the user first types.
Note, the system will show error after defining the setup functions, as it
requires the delegates to be included, and custom functions for text fields
and mandatory functions for tables to be defined.
Delegates to be included
The code described in Table 8-3 defines the delegates and data source
for the Search Display Table.
178
Chapter 8 Searching Data Screen
Table View Delegate and Data Source and Text Field Delegate is included
custom function to draw the text field on the screen, to be called when form is
loaded
2. view.addSubview(searchTextField)
3. commonFunctions.setFieldLayout(mainField: searchTextField,
constraintField: view!, topAnchor: 60, leftAnchor: 5,
rightAnchor: -5, heightAnchor: 40)
This code in Table 8-5 will draw the Table View on the screen. Initially,
the table will be invisible as this will be populated based on user search
criteria.
179
Chapter 8 Searching Data Screen
custom function to draw the search display table on the screen. To be called when
user starts the search
calculating the height of the table. Height of the row is 40, multiplying it with total
number of books searched
3. view.addSubview(searchTableView)
4. commonFunctions.setFieldLayout(mainField: searchTableView,
constraintField: searchTextField, topAnchor: 60, leftAnchor:
5, rightAnchor: -5, heightAnchor: tableHeight)
5. }
180
Chapter 8 Searching Data Screen
Table 8-6. Query Construct for Searching the User Searched Books
fileprivate func bookQuery(){
databaseFunctions.group.enter()
databaseFunctions.bookQuery()
databaseFunctions.group.notify(queue: .main) {
self.setup()
self.drawInputScreen()
}
}
2. databaseFunctions.group.enter()
Entering the group to wait on the main thread. It will be released after the query is
completed
3. databaseFunctions.bookQuery()
this is the custom function defined in DatabaseFunctions file (will see the details in
the next section)
(continued)
181
Chapter 8 Searching Data Screen
4. databaseFunctions.group.notify(queue: .main) {
Waiting on the thread. This will be discussed in detail in the next section
5. self.setup()
Once the query is over, we will call the setup function defined before
6. self.drawInputScreen()
and draw the screen to have a Search Field for user to search
7. }
8. }
182
Chapter 8 Searching Data Screen
operation.recordFetchedBlock = { record in
let book = Book()
book.bookname = record["bookname"]
book.authorname = record["authorname"]
book.genre = record["genre"]
book.status = record["status"]
self.books.append(book)
}
o peration.queryCompletionBlock = { [unowned self] (cursor,
error) in
DispatchQueue.main.async {
if let error = error {
print(error)
}else {
self.syncQueue.async {
self.group.leave()
}
}
}
}
}
(continued)
183
Chapter 8 Searching Data Screen
appending the book record to the class level books object array
13. }
14. operation.queryCompletionBlock = { [unowned self] (cursor,
error) in
when the query is completed, this block will be called – notice this is asynchronous
15. DispatchQueue.main.async {
a. print(error)
if error in executing the query the error will be printed on the console
17. }else {
a. self.syncQueue.async {
b. self.group.leave()
leave the wait on the group. This will return to the calling function of this bookQuery
function
c. }
18. }
19. }
20. }
21. }
185
Chapter 8 Searching Data Screen
• The third and the last one is called when editing ends,
this is for housekeeping; we will replace the book object
with the temporary book object so that it is reset to the
entire book list. This is required so that when the user
searches next time, it has all the books to search from.
186
Chapter 8 Searching Data Screen
Editing Begin
The code in Table 8-9 is about Text Field Edit Begin Function.
This function Is called when the Search Text field gets focus (become first
responder).
2. if(sender == searchTextField){
The sender is an input variable which has the reference to the current focus text
field. In this view controller, we only have one text field, but it is always a good
practice to check which Text field has triggered the event. Also note, we made the
association of the textfield to the event earlier in the setup.
3. searchbooks = databaseFunctions.books
When editing begins, we are storing the book class object to another variable of
type Book called searchBooks. As explained before, we use the books variable
for content display on Table View and we will replace its value with only the books
that match the search criteria. Please note, the books variable is populated with all
books when we query the database.
4. }
5. }
Editing Changed
The code in Table 8-10 is about Text Field Edit Changed Function.
187
Chapter 8 Searching Data Screen
188
Chapter 8 Searching Data Screen
189
Chapter 8 Searching Data Screen
6. databaseFunctions.books.append(contentsOf: searchbooks.filter {
We are going to do the same filter again, but this time, it is for the author name. So
that the user can search for a book and author name at the same time. Also note, we
are appending as we are adding on the search from the previous book name search
a. item in return item.authorname.lowercased().
contains(searchTextField.text!.lowercased())
same as before, the only change is we are searching for the author 's name.
7. })
8. removeTableConstraints()
This is a custom function to remove the table from the screen as we will redraw the
table with new records.
9. drawSearchTable()
This will redraw the Search Table.
10. if(databaseFunctions.books.count > 0){
After searching, all records are stored in the DatabaseFunctions class books
object. The following code is executed if the books variable has records
a. searchTableView.isHidden = false
if the condition is true, we will make the Search Table View visible
b. searchTableView.reloadData()
reload the table to display the searched records
11. }else{
a. searchTableView.isHidden = true
if the condition is false, we will hide the Search Table View
12. }
13. }
14. }
190
Chapter 8 Searching Data Screen
Editing End
The code in Table 8-11 is about Text Field Edit End Function.
Remove Constraints
If we need to redraw UI Objects, it is always a good practice to remove the
constraints of the objects; otherwise, we will get layout constraint errors.
Table 8-12 has the relevant details.
191
Chapter 8 Searching Data Screen
Table Functions
The code in Tables 8-13 and 8-14 shows two functions which must be
implemented for table views. For this view, we will only need these two
functions to be defined.
Total number of rows to Display
192
Chapter 8 Searching Data Screen
193
Chapter 8 Searching Data Screen
cell.detailTextLabel?.text = databaseFunctions.
books[indexPath.row].authorname
return cell
}
Built-in required table view function to render the row display. It returns a UI Table
View Cell Object.
We are defining a UI Table View Cell. The first argument is style, we are using right
and left align. On right will be the main text (book name) on right it will be the
detailed text (Author Name). The second argument is an identifier of the cell. In the
setup function, we gave our cell identifier name as “searchcell”
retrieving the current book name based on the “indexPath” object row variable,
(This will loop through each available book in the array) and applying the attribute
style we created in the previous step
(continued)
194
Chapter 8 Searching Data Screen
Run the program and it should show a screen like in Figure 8-1.
195
Chapter 8 Searching Data Screen
The first screen in Figure 8-1 is the display screen which shows all books.
The second screen in Figure 8-1 is the search screen. Notice the letter Typed is “T”,
the result shows every book or author that has the letter “T” or “t”.
The Third screen in Figure 8-1 we typed an e as well and it displays any book or
author that has “te” or “Te” or “TE” or “tE”
Summary
In this chapter, we learned how to conditionally search iCloud records and
display them for further action. We also learned how to make dynamic
search work using the Text Field Edit functions.
196
Chapter 8 Searching Data Screen
This chapter concludes our Part I of the book. If you have reached
here and tried everything written so far, you have learned the following
concepts:
197
CHAPTER 9
App Development
Part 2 Overview
If you have reached here, congratulations. You have acquired the required
basic knowledge to create your own iPhone App and now are some more
advanced tasks. That is quite a feat, and you should be proud to make
it thus far. What you have learned until now are the core skills, but not
enough for creating a professional-looking App. Besides, there are many
features that Xcode offers to make your App well integrated into the
Apple ecosystem. In this short chapter, we’ll review what lies ahead in the
remainder of the book and then start our iCloud set-up.
iCloud Setup
Before we begin designing our App, let us go ahead and define the tables to
persist our data. We will need the following two tables:
200
Chapter 9 App Development Part 2 Overview
Book This will store all book records ( This was explained in Part I)
Booknotes This table will store book notes by for each relevant Book
Figure 9-1 shows the Book table attributes and indexes (indexes are
important for enabling querying, sorting, and searching).
The Book notes table shown in Figure 9-2 will have the following
attributes and indexes (indexes are important for enabling querying,
sorting, and searching).
201
Chapter 9 App Development Part 2 Overview
Summary
This chapter provided us with an introduction to Part II of our App
development journey. The chapter outlined everything we would learn
in the next 12 chapters. In the next part of our App creation journey, we
would need an additional table, called Book Notes, to persist all notes
about a book in iCloud repository – the chapter provided the details of the
Book Notes table.
202
PART II
Overview
CHAPTER 10
Redesigning the
Display Screen
In this chapter, we will redesign the screen to uplift the UI, as shown in
Figure 10-1.
The blocks on the Top will show the total books available by reading
status. The block shows an icon, a reading status title, and the total
count. Following immediately, we have all books displayed nicely with
customized cells. We will have an option of making a book Favorite; in
such a case, it will be shown under the favorite book list below the books
206
Chapter 10 Redesigning the Display Screen
by status block. Also, note we have added a new Tab bar icon “Shared
Books”, which will show books shared by others. We will now learn how to
create this screen. The new concepts we will learn are as follows:
• Manipulating Views
207
Chapter 10 Redesigning the Display Screen
208
Chapter 10 Redesigning the Display Screen
209
Chapter 10 Redesigning the Display Screen
210
Chapter 10 Redesigning the Display Screen
The code in Table 10-2 is a repeat of the previous block to define the
next two top view blocks to show Books Currently Reading and Book To
be Read in the future. Please note, the system icons are changing, and the
view color is set to Gray.
211
Chapter 10 Redesigning the Display Screen
label.textColor = .black
label.textAlignment = .right
return label
}()
let readingDigitLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .right
return label
}()
//MARK: - Top View UI Object - TO-BE-READ BLOCK VIEW
lazy var toBeReadButton:UIButton = {
let uiButton = UIButton()
uiButton.translatesAutoresizingMaskIntoConstraints = false
uiButton.layer.cornerRadius = 5
uiButton.layer.borderWidth = 1
uiButton.layer.borderColor = UIColor.black.cgColor
u iButton.layer.borderColor = UIColor(red: 0/255,
green: 0/255, blue: 0/255, alpha: 1).cgColor
uiButton.layer.shadowRadius = 5
uiButton.layer.shadowOffset = .zero
uiButton.layer.shadowOpacity = 1
u iButton.addTarget(self, action: #selector(tobeReadButton
Action), for: .touchUpInside)
return uiButton
}()
212
Chapter 10 Redesigning the Display Screen
213
Chapter 10 Redesigning the Display Screen
We will also define two table views for displaying books by status and
books marked as favorite:
214
Chapter 10 Redesigning the Display Screen
Class-Level Variables
We will define two class-level variables to access functions from our
custom DatabaseFunctions class (for all iCloud CRUD functions)
and CommonFunctions(for screen drawing functions). Besides these
two variables, we will also define three arrays of type Book (Defined in
DatabaseFunctions class) to hold books for the three top views (Read,
Reading, and To-Be-Read). bookTitle variable stores the Title for the Book
View Table and the cloudKitTask variable is to accept books shared from
other users (we will discuss this in the latter part of this book). Table 10-3
has the relevant code details.
215
Chapter 10 Redesigning the Display Screen
Lifecycle Method
We will use the Default View Will Appear method as our lifecycle method,
and simply call a custom function booksQuery() to get the books from the
iCloud repository. The code details are in Table 10-4.
Query Books
In the DatabaseFunctions Class, we have a function we have written
before to get all books from the iCloud repository. Reference to the same
is given in Table 10-5. Note that it goes against the user private database
so, only the user-specific books are retrieved. Also note we are storing all
books which have the favorite value set to 1 in the favbooks Array.
216
Chapter 10 Redesigning the Display Screen
operation.zoneID = ckRecordZoneID.zoneID
operation.recordFetchedBlock = { record in
let book = Book()
book.bookname = record["bookname"]
book.authorname = record["authorname"]
book.genre = record["genre"]
book.status = record["status"]
book.reminder = record["reminder"]
book.favorite = record["favorite"]
book.recordID = record.recordID
if(book.favorite == 1){
self.favbooks.append(book)
}
self.books.append(book)
}
operation.queryCompletionBlock = { [unowned self] (cursor,
error) in
DispatchQueue.main.async {
if let error = error {
print(error)
}else {
self.syncQueue.async {
self.group.leave()
}
}
}
}
}
217
Chapter 10 Redesigning the Display Screen
Back to our Books View Controller, we will define the Query Book
functions. In the query books function, we will asynchronously wait until
the iCloud finishes the query. Upon completion, we will create three
different variables of type Book (defined in DatabaseFunctions class), and
store the respective book details based on the book status (Read, Reading,
or To-Be-Read). Relevant code on Book Query is in Table 10-6.
218
Chapter 10 Redesigning the Display Screen
219
Chapter 10 Redesigning the Display Screen
220
Chapter 10 Redesigning the Display Screen
15. self.setup()
After the queries are set we will call our custom setup functions, Explained in the
next section
16. self.topScreen()
We will draw the top of the screen with three block views
17. self.drawBookDisplay()
We will draw the table for displaying the book titles
18. self.bookTableView.reloadData()
Reload the table to display all books
19. }
20. }
Setup Function
We will do two things in setup. Set the background color of the view to
white and set the delegate, data source, and cell definition for the main
table to display books, and the favorite table to display the books marked
as favorite. The Cell we have used so far is the default Cell definition of
table view (UITableViewCell), while for our favorite table we will use the
default cell, for the main book display, we will use a custom cell class. We
will learn about the custom cell in the Table Section. Also, note as soon as
we set the delegate and the data source for the table, we will get two errors.
First, we need to make the View Controller extend the Table View Delegate
and Table View Data Source. Second, there are two mandatory table
functions that we need to write. It is ok to have errors for now. The error
will go away after completing the table section. Table 10-7 has the relevant
code details.
221
Chapter 10 Redesigning the Display Screen
222
Chapter 10 Redesigning the Display Screen
223
Chapter 10 Redesigning the Display Screen
224
Chapter 10 Redesigning the Display Screen
225
Chapter 10 Redesigning the Display Screen
226
Chapter 10 Redesigning the Display Screen
227
Chapter 10 Redesigning the Display Screen
228
Chapter 10 Redesigning the Display Screen
229
Chapter 10 Redesigning the Display Screen
230
Chapter 10 Redesigning the Display Screen
Table Setup
Custom Cell
Defining a Custom Cell
In designing iPhone applications, table view plays a crucial role.
Aesthetically appealing screens usually have images, text with varying font
designs, graphs, and blocks. And in achieving it, table view cells play a
critical role. A Cell is a view. We can design this view to have any UI Object
organized and arranged to uplift the UI. In this example, we are creating a
simple view. We will have space between cells, and the Book name will be
centrally aligned within a raised button with a blue background.
As explained, a cell is a class. So, go ahead and create a class of Type
UITableViewCell and name it BookTableViewCell. See the screen details
in Figure 10-2 for reference.
233
Chapter 10 Redesigning the Display Screen
234
Chapter 10 Redesigning the Display Screen
235
Chapter 10 Redesigning the Display Screen
Cell for Row At – This function renders the cell with the book name.
This function is an iterative one and, depending on the number of rows
we have, the function will repeat to show the book rows. This is also
the function where we will refer to the custom cell and access the UI
Objects that we have defined in the cell definition. The code is defined in
Table 10-14.
236
Chapter 10 Redesigning the Display Screen
237
Chapter 10 Redesigning the Display Screen
3. l et cell = tableView.dequeueReusableCell(withIdentifier:
"book", for: indexPath) as! BookTableViewCell
The cell is defined using the table view Dequeue Reusable cell. System resources
are costly and dequeue cells help to repurpose the cell that goes off-screen. So, in
other words, if we have 100 books and the screen can only show 20 books, as the
user scrolls to display the next set of 20 books, the previously displayed cells are
repurposed as they go off screen. Also notice we are defining this as a custom cell
so adding as! (forcing) to Book Table View Cell, which we have defined before.
4. c ell.bookLabelButton.setTitle(self.databaseFunctions.
books[indexPath.row].bookname, for: .normal)
the custom cell we have defined has a button with the name bookLabelButton
(see the cell definition section). We are using the UI Button setTitle to set the title
of the button as the current row book name. (All books stored in books variable in
databaseFunctions class and the indexpath.row provide the current book index.)
5. c ell.bookLabelButton.addTarget(self, action:
#selector(taskButtonAction), for: .touchUpInside)
Notice the function taskButtonAction added to the button for .touchupinside event.
This function will be invoked when the button is pressed on each cell to show the
details of the book. For now, we will get an error as the function is not yet defined.
6. c ell.bookLabelButton.tag = indexPath.row
When the button on a cell is called, we need to know which book needs to be
displayed. Tag is an integer variable, and we are using this to store the current row
number to later retrieve the necessary book details
7. return cell
returning the cell for display
8. }else{
if the tableview is not for displaying the Book by status, the following statements will
be executed for showing the books marked as favorites
(continued)
238
Chapter 10 Redesigning the Display Screen
Title for Header in Section – This will show the header of the table. We
will return a label based on which block is selected from the top part of the
screen (Books Read, Reading Now, and To Be Read. The relevant code is
defined in Table 10-15.
239
Chapter 10 Redesigning the Display Screen
Height for Row At: Since in our custom cell the height of the button
is 40 and we need to leave space between two cells, we want to make row
height more than the default 40 pixels. For the Favorite book it is set to the
standard 40 Pixel. The relevant code is defined in Table 10-16.
240
Chapter 10 Redesigning the Display Screen
When the Favorite book cell is clicked, we need to show the details of
the book, as shown in Table 10-17.
241
Chapter 10 Redesigning the Display Screen
242
Chapter 10 Redesigning the Display Screen
Calling the Book Detail Screen When the Custom Cell Button
Is Clicked
243
Chapter 10 Redesigning the Display Screen
244
Chapter 10 Redesigning the Display Screen
245
Chapter 10 Redesigning the Display Screen
Reading Button Action: This function is called when the user clicks
on the second view to show books that user is currently reading. Recall
when we queried the table, we filtered on the book records for status
“reading” and stored it in a variable “reading” of type Book class defined
in DatabaseFunctions class. The relevant code details are in Table 10-18.
246
Chapter 10 Redesigning the Display Screen
Read Button Action: This function is called when the user clicks
on the First view to show books that user is currently reading. Recall
when we queried the table, we filtered on the book records for status
“read” and stored it in a variable “reading” of type Book class defined in
DatabaseFunctions class. The relevant code details are in Table 10-19.
247
Chapter 10 Redesigning the Display Screen
248
Chapter 10 Redesigning the Display Screen
Custom Delegation
Delegations allow objects to communicate back to the source of its
origin in a decoupled way. Swift inherently uses Delegation for all its
objects – example is the return button on the Text field keyboard. When we
implement the default Text field delegate in a view controller, whenever
the return key is pressed, a system-defined delegate function is called, and
if it is implemented, the code is executed accordingly.
In this section, we are going to learn how to create a custom delegate.
Before we go ahead, let us understand the use case. In our App, from the
Main Display screen, user can tap on a book and can launch the Book
Detail screen where following book specific actions can be taken.
249
Chapter 10 Redesigning the Display Screen
250
Chapter 10 Redesigning the Display Screen
251
Chapter 10 Redesigning the Display Screen
252
Chapter 10 Redesigning the Display Screen
253
Chapter 10 Redesigning the Display Screen
We have discussed these two functions in the Main Display View controller before.
The first function is called when the user taps on the Book name in the main display
and the second function is called when the user taps a book in the favorite table.
In both the functions, the variable delegateBooksViewRefresh, which is of the
custom type RefreshBooksViewProtocol, is set to self, indicating the handle to the
delegate. This is important to get access to the delegate functions.
254
Chapter 10 Redesigning the Display Screen
255
Chapter 10 Redesigning the Display Screen
Summary
In this chapter, we redesigned our Display Books View Controller.
Customizing Table View Cell to display other UI Objects was a critical part
of learning in this chapter. We also learned about Tap Gestures to add our
own events on UI Objects which don’t have built-in events. Finally, we
learned about delegates – as we move between screens, we need to pass
values across views – delegates help us to achieve the same.
In the next chapter, we will learn about revamping the Add
Book screen.
256
CHAPTER 11
Adding a Book
This chapter will provide details on how to add a book to our library. We
will create a new view controller for adding a book and link it to the bottom
tab bar, as shown in Figure 11-1.
The Add Book will take the following information from the user:
All inputs are mandatory. Save won’t work if any of the values is
missing. The screen layout for Adding a book is shown in Figure 11-2.
Execute the following steps to complete the functions.
258
Chapter 11 Adding a Book
259
Chapter 11 Adding a Book
Inheriting Delegates
This view controller will inherit the table Delegate, table data source, and
Text Field Delegates. The Table delegate and data source is to display
a table for Genre and Status, and the Text Field delegate is to make the
keyboard disappear when return is pressed. Class inheritance code is in
Table 11-1.
Declaring Variables
We will declare the following class variables as outlined in Table 11-2.
260
Chapter 11 Adding a Book
261
Chapter 11 Adding a Book
• Two Input Text Field for Book and Author Name Input,
as shown in Table 11-4.
262
Chapter 11 Adding a Book
263
Chapter 11 Adding a Book
264
Chapter 11 Adding a Book
265
Chapter 11 Adding a Book
266
Chapter 11 Adding a Book
Setup Function
View Did Load function code details are in Table 11-8.
267
Chapter 11 Adding a Book
statusTableView.layer.borderColor = UIColor.white.
cgColor
statusTableView.separatorStyle = .none
commonVariables.setGenres()
commonVariables.setStatuses()
bookTextField.delegate = self
authorTextField.delegate = self
}
The Table setup we have gone through before, so we are going to skip the
explanation. We are also using the default Cell Type for the table cell.
There are two functions defined in the CommonVariables Calls, which will populate
the Genre and Status Array object. The following section will explain the functions
268
Chapter 11 Adding a Book
269
Chapter 11 Adding a Book
270
Chapter 11 Adding a Book
271
Chapter 11 Adding a Book
gd.genresubname = "Romance"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Women’s Fiction"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "LGBTQ+"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Contemporary Fiction"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Literary Fiction"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Magical Realism"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Science Fiction"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Graphic Novel"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Short Story"
g.genredetail.append(gd)
gd.genresubname = "Young Adult"
(continued)
272
Chapter 11 Adding a Book
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "New Adult"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Children’s"
g.genredetail.append(gd)
gd = GenreDetail()
genres.append(g)
g = Genre()
g.genrename = "Non Fiction"
gd = GenreDetail()
gd.genresubname = "Memoir & Autobiography"
g.genredetail = [GenreDetail]()
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Biography"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Food & Drink"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Art & Photography"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Self-help"
g.genredetail.append(gd)
(continued)
273
Chapter 11 Adding a Book
gd = GenreDetail()
gd.genresubname = "History"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Travel"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "True Crime"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Humor"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Essays"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Guide or How-to"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Religion & Spirituality"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Humanities & Social Sciences"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Parenting & Families"
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Science & Technology"
(continued)
274
Chapter 11 Adding a Book
g.genredetail.append(gd)
gd = GenreDetail()
gd.genresubname = "Children’s"
g.genredetail.append(gd)
genres.append(g)
}
This function stores the genre names against two main categories: Fiction and
Nonfiction.
275
Chapter 11 Adding a Book
Draw Screen
Now, let us come back to our AddBookViewController class and add the
function as shown in Table 11-12.
276
Chapter 11 Adding a Book
commonFunctions.setFieldLayout(mainField:
genreTableView, constraintField: genreTextField,
topAnchor: 40, leftAnchor: 0, rightAnchor: 0,
heightAnchor: 200)
view.addSubview(statusTextField)
commonFunctions.setFieldLayout(mainField:
statusTextField, constraintField: genreTableView,
topAnchor: 220, leftAnchor: 0, rightAnchor: 0,
heightAnchor: 40)
view.addSubview(statusTableView)
commonFunctions.setFieldLayout(mainField:
statusTableView, constraintField: statusTextField,
topAnchor: 40, leftAnchor: 0, rightAnchor: 0,
heightAnchor: 200)
}
This function is called next after the setup function, which will draw the objects on
the screen as shown in the following.
277
Chapter 11 Adding a Book
278
Chapter 11 Adding a Book
Table Sections
This code in Table 11-13 is required for the Genre Table. We have two
sections of Genre: Fiction and Non-Fiction. The following code instructs
the Genre Table View that there exist two display sections.
Table Rows
Both tables need to inform the Table View how many rows are there
for Genre (in each section) and status table. The relevant code is in
Table 11-14.
279
Chapter 11 Adding a Book
280
Chapter 11 Adding a Book
281
Chapter 11 Adding a Book
Table Header
We are going to design the Header of each table (Section Header) – this
code provides detail on the same. Notice that everything in a table is a
view. We are adding label objects to the header view and enhancing the
font and size for the header. Relevant code is in Table 11-17.
282
Chapter 11 Adding a Book
283
Chapter 11 Adding a Book
284
Chapter 11 Adding a Book
Visual Display
Code for Editing Did begin is defined in Table 11-20.
285
Chapter 11 Adding a Book
286
Chapter 11 Adding a Book
287
Chapter 11 Adding a Book
}else{
bookTextField.layer.borderWidth = 0
}
if(authorTextField.text == ""){
a uthorTextField.placeholder = "Author Name cannot be
blank"
authorTextField.layer.borderWidth = 2
authorTextField.layer.cornerRadius = 5
authorTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
}else{
authorTextField.layer.borderWidth = 0
}
if(genreTextField.text == ""){
genreTextField.placeholder = "Genre cannot be blank"
genreTextField.layer.borderWidth = 2
genreTextField.layer.cornerRadius = 5
genreTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
}else{
genreTextField.layer.borderWidth = 0
}
if(statusTextField.text == ""){
statusTextField.placeholder = "Status cannot be blank"
statusTextField.layer.borderWidth = 2
statusTextField.layer.cornerRadius = 5
statusTextField.layer.borderColor = UIColor.red.cgColor
canSave = false
(continued)
288
Chapter 11 Adding a Book
}else{
statusTextField.layer.borderWidth = 0
}
if(canSave){
book.bookname = bookTextField.text!
book.authorname = authorTextField.text!
book.genre = genreTextField.text!
book.status = statusTextField.text!
book.favorite = 0
saveBook()
}
}
1. @objc func saveButtonAction(sender: UIButton){
This function is linked to the save button of the Navigation Bar
2. var canSave:Bool = true
This variable will be set to false if the text fields are blank or the entered book name
is not accurate.
3. if(bookTextField.text == ""){
4. bookTextField.placeholder = "Book Name cannot be blank"
5. bookTextField.layer.borderWidth = 2
6. bookTextField.layer.cornerRadius = 5
7. bookTextField.layer.borderColor = UIColor.red.cgColor
8. canSave = false
9. }else{
10. bookTextField.layer.borderWidth = 0
11. }
(continued)
289
Chapter 11 Adding a Book
If the book name is blank – canSave variable is set to false, book saving is aborted,
and the book name text field border color is set to red and placeholder text states
“Book Name can’t be blank”
12. if(authorTextField.text == ""){
13. a uthorTextField.placeholder = "Author Name cannot be
blank"
14. authorTextField.layer.borderWidth = 2
15. authorTextField.layer.cornerRadius = 5
16. authorTextField.layer.borderColor = UIColor.red.cgColor
17. canSave = false
18. }else{
19. authorTextField.layer.borderWidth = 0
20. }
If the author’s name is blank – canSave variable is set to false, book saving is
aborted, and the author’s name text field border color is set to red and placeholder
text states “Author Name can’t be blank”
21. if(genreTextField.text == ""){
22. genreTextField.placeholder = "Genre cannot be blank"
23. genreTextField.layer.borderWidth = 2
24. genreTextField.layer.cornerRadius = 5
25. genreTextField.layer.borderColor = UIColor.red.cgColor
26. canSave = false
27. }else{
28. genreTextField.layer.borderWidth = 0
29. }
If the Genre is blank – canSave variable is set to false, book saving is aborted,
and the Genre name text field border color is set to red and placeholder text states
“Genre can’t be blank”
(continued)
290
Chapter 11 Adding a Book
291
Chapter 11 Adding a Book
Saving to iCloud
The code defined in Table 11-23 will call the iCloud asynchronous function
to save the book record in the iCloud private repository of the user and
wait for completion.
292
Chapter 11 Adding a Book
293
Chapter 11 Adding a Book
294
Chapter 11 Adding a Book
295
Chapter 11 Adding a Book
296
Chapter 11 Adding a Book
297
Chapter 11 Adding a Book
else{
self.syncQueue.async {
self.group.leave()
}
}
}
})
}
1. func saveBook(book:Book){
The custom function to save the book to iCloud
2. let ckRecordZoneID = CKRecordZone(zoneName: "SharedZone")
We will store this in a custom zone – we are naming this zone as SharedZone. This
is important if we want to share books with other users, as the default zone restricts
any sharing options.
3. // Save the zone in the private database
4. let container = CKContainer.default()
5. let privateDB = container.privateCloudDatabase
6. privateDB.save(ckRecordZoneID){ zone, error in
7. if let error = error{
8. print("Zone creation error: \(String(describing: error))")
9. }else{
10. print("Zone created: \(zone!)")
11. }
12. }
The preceding segment of code will create a SharedZone if it is not already existing.
13. let privateDatabase = CKContainer.default().
privateCloudDatabase
14. let ckRecordID = CKRecord.ID(zoneID: ckRecordZoneID.zoneID)
(continued)
298
Chapter 11 Adding a Book
299
Chapter 11 Adding a Book
Reset Fields
This is linked to the reset navigation bar tab bar button. As outlined in
Table 11-27, when clicked, it clears all input fields by assigning it an
empty string.
Summary
In this chapter, we revamped our Add Book Screen. We learned how to
make a drop-down dynamic table view for Book Genre and Book Status
input fields. We also learned how to use Group Table view display when
we showed genre grouped under Fiction and Nonfiction in the Genre
TableView selection drop-down.
In the next chapter, we will display the details of the Book and action
we can take when a user taps on any book. This feature was not available in
our Basic App.
300
CHAPTER 12
Initial Setup
Create the View Controller
Create a view Controller with the name “BookViewController.”
Class Inheritance
Beside the default UIViewController, we need to inherit the following
delegates:
Class Variables
We will need the following types of variables
302
Chapter 12 Book Details View Controller
303
Chapter 12 Book Details View Controller
Setup
Table 12-3. Setup Function
/*
The setup function is used to do the following
- the background of the view is set to white
- setup the delegate for the tab bar
- set the book table view, which uses a custom cell for
displaying the book details.
- Call for a function setBookDetailswhcih will put the
book fields data in a two dimensional array
- Two Tap Gesture event defined for the reminder screen
button - save and reset
*/
(continued)
304
Chapter 12 Book Details View Controller
setBookDetails()
let tapGestureRecognizerReminder =
UITapGestureRecognizer(target: self, action: #selector
(reminderImageTapped(tapGestureRecognizer:)))
reminderPopUpImageView.isUserInteractionEnabled = true
reminderPopUpImageView.addGestureRecognizer(tapGesture
RecognizerReminder)
let tapGestureRecognizerResetReminder =
UITapGestureRecognizer(target: self, action: #selector
(reminderResetImageTapped(tapGestureRecognizer:)))
reminderResetImageView.isUserInteractionEnabled = true
reminderResetImageView.addGestureRecognizer(tapGesture
RecognizerResetReminder)
}
(continued)
305
Chapter 12 Book Details View Controller
306
Chapter 12 Book Details View Controller
13. r eminderPopUpImageView.addGestureRecognizer(tapGesture
RecognizerReminder)
14. l et tapGestureRecognizerResetReminder = UITapGesture
Recognizer(target: self, action: #selector(reminderReset
ImageTapped(tapGestureRecognizer:)))
15. reminderResetImageView.isUserInteractionEnabled = true
16. r eminderResetImageView.addGestureRecognizer(tapGesture
RecognizerResetReminder)
Setting up the tap gesture event for the Save button and Reset button on the
reminder pop up. This is required because Image does not have any system-defined
events. The method reminderImageTapped is a custom method that needs to be
defined.
17. }
307
Chapter 12 Book Details View Controller
value = BookDetails()
detail = []
detail.append(book.authorname!)
value.label = "Author"
value.value = detail
bookdetails.append(value)
value = BookDetails()
detail = []
value.label = "Genre"
if(book.genre != nil){
let local = book.genre.split(separator: ",")
value.value = detail
bookdetails.append(value)
value = BookDetails()
detail = []
(continued)
308
Chapter 12 Book Details View Controller
bookTableView.reloadData()
}
The Table in the Book Detail view will show all the Book Data – Book Name, Author
Name, Genre, and Status. We want to show it like a table, and table loops through
an array object to display all the values. This function creates an array for all book
attributes. The array will have both the title and its respective value(s).
1. func setBookDetails(){
This is the custom function called from the previously explained setup function
2. bookdetails.removeAll()
This variable is defined at the class level, and it is of type BookDetails custom Struct
that we have defined at the class (See the variable section)
3. var value = BookDetails()
4. var detail:[String] = []
5. detail.append(book.bookname!)
6. value.label = "Book Name"
7. value.value = detail
8. bookdetails.append(value)
We are populating the Book name in the first occurrence of the variable. The First
row is the title of the attribute (in this case, it is “Book Name”) and the next row
is the actual book name. The temp variable is added to the class level bookDetails
variable.
9. value = BookDetails()
10. detail = []
11. detail.append(book.authorname!)
(continued)
309
Chapter 12 Book Details View Controller
310
Chapter 12 Book Details View Controller
28. detail.append(book.status!)
29. value.label = "Status"
30. value.value = detail
31. bookdetails.append(value)
We are populating the Book Status as the final occurrence of the variable. The First
row is the title of the attribute (in this case, it is “Status”) and the next row is the
actual status. The temp variable is added to the class level bookDetails variable.
32. bookTableView.reloadData()
reloading the table to reflect the changes
33. }
311
Chapter 12 Book Details View Controller
Screen Objects
The section provides the details of the UI Objects we will need to draw the
screen. The Objects are defined at the class level so that all methods can
access them when required. The Ui Object details of the Book Detail View
Controller are in Table 12-5.
312
Chapter 12 Book Details View Controller
313
Chapter 12 Book Details View Controller
let bookLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
314
Chapter 12 Book Details View Controller
Table 12-7 has details on all six blocks on the top of the Book detail
screen, each denoting a particular function.
Share Block This is to display the Share block. Button is for the block
Share Button frame, Image to display the Icon, and label to display the
Share Image View label text.
Share Text Label
Edit Block This is to display the Edit block. Button is for the block
Share Button frame, Image to display the Icon and label to display the
Share Image View label text
Share Text Label
Delete Block This is to display the Delete block. Button is for the block
Share Button frame, Image to display the Icon and label to display the
Share Image View label text
Share Text Label
Book Notes Block This is to display the Book Notes block. Button is for the
Share Button block frame, Image to display the Icon and label to display
Share Image View the label text
Share Text Label
Reminder Block This is to display the Reminder block. Button is for the block
Share Button frame, Image to display the Icon and label to display the
Share Image View label text
Share Text Label
Favorite Block This is to display the Favorite block. Button is for the block
Share Button frame, Image to display the Icon and label to display the
Share Image View label text
Share Text Label
315
Chapter 12 Book Details View Controller
316
Chapter 12 Book Details View Controller
let shareTextLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
button.layer.shadowRadius = 5
button.layer.shadowOffset = .zero
button.layer.shadowOpacity = 1
b utton.addTarget(self, action: #selector(completeButton
Action), for: .touchUpInside)
return button
}()
let editTextLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
(continued)
318
Chapter 12 Book Details View Controller
319
Chapter 12 Book Details View Controller
let deleteTextLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
320
Chapter 12 Book Details View Controller
let notesTextLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
321
Chapter 12 Book Details View Controller
let reminderTextLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
(continued)
322
Chapter 12 Book Details View Controller
323
Chapter 12 Book Details View Controller
let favTextLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
Table 12-9 has the definition of the Table view, which will display the
Book detail and popup view for setting reminder.
Table View Table view for displaying the current Book Details (Book Name, Author
Name, Genre, and reading status)
Popup We will need this popup view when the user taps on the Reminder
View Block, this view will display a date control for setting the reminder.
Large The Large Popup view is used to cover the entire screen with a light
Popup gray shade to avoid users from accidentally clicking other action
View buttons on the backdrop screen when a reminder is displayed.
324
Chapter 12 Book Details View Controller
325
Chapter 12 Book Details View Controller
Reminder Label This is for the Reminder popup screen to set the reminder.
This is a UI label to display the reminder prompt text
Reminder Picker Date Picker for setting the reminder
Reminder Image View This button will save the reminder for the book in the
iCloud repository.
Remind Rest Image This is the reset button to set the date to a back date so
View that no reminder is set (reminder works always for a future
date)
326
Chapter 12 Book Details View Controller
return datePicker
}()
let reminderPopUpImageView: UIImageView = {
let theImageView = UIImageView()
t heImageView.image = UIImage(systemName: "checkmark.
square.fill")
t heImageView.translatesAutoresizingMaskIntoConstraints =
false
return theImageView
}()
327
Chapter 12 Book Details View Controller
Drawing Screen
The code defined in Table 12-13 draws the six blocks on the top, table view
for book details in the middle and the tab bar at the bottom of the screen.
func taskScreen(){
let screensize: CGRect = UIScreen.main.bounds
let screenHeight = screensize.height
view.addSubview(bookLabel)
c ommonFunctions.setFieldLayout(mainField: bookLabel,
constraintField: view!, topAnchor: 20, leftAnchor: 0,
rightAnchor: 0, heightAnchor: 80)
(continued)
328
Chapter 12 Book Details View Controller
b ookLabel.attributedText = NSAttributedString(string:
book.bookname.capitalized, attributes: attributesMain)
view.addSubview(shareButton)
c ommonFunctions.setFieldLayout(mainField: shareButton,
constraintField: view!, topAnchor: 100, leftAnchor: 10,
rightAnchor: -270, heightAnchor: 80)
view.addSubview(shareImageView)
c ommonFunctions.setFieldLayout(mainField: shareImageView,
constraintField: shareButton, topAnchor: 5, leftAnchor:
40, rightAnchor: -40, heightAnchor: 30)
view.addSubview(shareTextLabel)
c ommonFunctions.setFieldLayout(mainField: shareTextLabel,
constraintField: shareButton, topAnchor: 40, leftAnchor:
2, rightAnchor: -2, heightAnchor: 30)
s hareTextLabel.attributedText = NSAttributedString(string:
"Share", attributes: attributesText)
view.addSubview(editButton)
c ommonFunctions.setFieldLayout(mainField: editButton,
constraintField: view!, topAnchor: 100, leftAnchor: 150,
rightAnchor: -140, heightAnchor: 80)
view.addSubview(editImageView)
c ommonFunctions.setFieldLayout(mainField: editImageView,
constraintField: editButton, topAnchor: 5, leftAnchor: 30,
rightAnchor: -30, heightAnchor: 30)
view.addSubview(editTextLabel)
c ommonFunctions.setFieldLayout(mainField: editTextLabel,
constraintField: editButton, topAnchor: 40, leftAnchor: 2,
rightAnchor: -2, heightAnchor: 30)
(continued)
329
Chapter 12 Book Details View Controller
e ditTextLabel.attributedText = NSAttributedString(string:
"Edit", attributes: attributesText)
s elf.editImageView.image = UIImage(systemName:
"checkmark")
view.addSubview(deleteButton)
c ommonFunctions.setFieldLayout(mainField: deleteButton,
constraintField: view!, topAnchor: 100, leftAnchor: 280,
rightAnchor: -10, heightAnchor: 80)
view.addSubview(deleteImageView)
c ommonFunctions.setFieldLayout(mainField: deleteImageView,
constraintField: deleteButton, topAnchor: 5, leftAnchor:
35, rightAnchor: -30, heightAnchor: 30)
view.addSubview(deleteTextLabel)
c ommonFunctions.setFieldLayout(mainField: deleteTextLabel,
constraintField: deleteButton, topAnchor: 40,
leftAnchor: 2, rightAnchor: -2, heightAnchor: 30)
d eleteTextLabel.attributedText =
NSAttributedString(string: "Delete", attributes:
attributesText)
view.addSubview(notesButton)
c ommonFunctions.setFieldLayout(mainField: notesButton,
constraintField: view!, topAnchor: 200, leftAnchor: 10,
rightAnchor: -270, heightAnchor: 80)
view.addSubview(notesImageView)
(continued)
330
Chapter 12 Book Details View Controller
c ommonFunctions.setFieldLayout(mainField: notesImageView,
constraintField: notesButton, topAnchor: 5, leftAnchor:
40, rightAnchor: -40, heightAnchor: 30)
view.addSubview(notesTextLabel)
c ommonFunctions.setFieldLayout(mainField: notesTextLabel,
constraintField: notesButton, topAnchor: 40,
leftAnchor: 2, rightAnchor: -2, heightAnchor: 30)
n otesTextLabel.attributedText = NSAttributedString(string:
"Book Notes", attributes: attributesText)
view.addSubview(reminderButton)
c ommonFunctions.setFieldLayout(mainField: reminderButton,
constraintField: view!, topAnchor: 200, leftAnchor: 150,
rightAnchor: -140, heightAnchor: 80)
view.addSubview(reminderImageView)
c ommonFunctions.setFieldLayout(mainField: reminderImage
View, constraintField: reminderButton, topAnchor: 5,
leftAnchor: 35, rightAnchor: -40, heightAnchor: 30)
view.addSubview(reminderTextLabel)
c ommonFunctions.setFieldLayout(mainField: reminderText
Label, constraintField: reminderButton, topAnchor: 40,
leftAnchor: 2, rightAnchor: -2, heightAnchor: 30)
r eminderTextLabel.attributedText = NSAttributedString
(string: "Reminder", attributes: attributesText)
view.addSubview(favButton)
c ommonFunctions.setFieldLayout(mainField: favButton,
constraintField: view!, topAnchor: 200, leftAnchor: 280,
rightAnchor: -10, heightAnchor: 80)
(continued)
331
Chapter 12 Book Details View Controller
if(book.favorite == 1){
favButton.backgroundColor = .lightGray
favTextLabel.backgroundColor = .lightGray
}else{
favButton.backgroundColor = .white
favTextLabel.backgroundColor = .white
}
view.addSubview(bookTableView)
c ommonFunctions.setFieldLayout(mainField: bookTableView,
constraintField: view!, topAnchor: 300, leftAnchor: 5,
rightAnchor: -5, heightAnchor: screenHeight - 400)
view.addSubview(tabBar)
c ommonFunctions.setFieldLayout(mainField: tabBar,
constraintField: view!, topAnchor: screenHeight - 80,
leftAnchor: 0, rightAnchor: 0, heightAnchor: 60)
tabBar.unselectedItemTintColor = .blue
}
332
Chapter 12 Book Details View Controller
Number of Sections
The table will have four sections, one each for Book Name, Author Name,
Genre, and Reading Status. Table 12-14 has the relevant code to let the
table know how many sections are there in the table.
Number of Rows
Every section will only have one row. However, the genre can have more
than one genre (currently not Implemented). Table 12-15 has all the
relevant code.
333
Chapter 12 Book Details View Controller
334
Chapter 12 Book Details View Controller
switch bookdetails[indexPath.section].label{
case "Book Name":
c ell.leftImageView.image = UIImage(systemName: "book.
fill")
case "Author":
c ell.leftImageView.image = UIImage(systemName:
"person")
(continued)
335
Chapter 12 Book Details View Controller
case "Genre":
c ell.leftImageView.image = UIImage(systemName:
"pencil")
default:
c ell.leftImageView.image = UIImage(systemName:
"square.and.pencil")
}
return cell
}
Row Height
We are setting every row height to 40 pixels, as shown in Table 12-17.
336
Chapter 12 Book Details View Controller
Header View
The section header will have a custom label to display the section label
bold and with a large font size as shown in Table 12-18
headerView.addSubview(label)
return headerView
}
337
Chapter 12 Book Details View Controller
Header Height
We are setting the height of the header to 40 Pixels, as shown in
Table 12-19.
338
Chapter 12 Book Details View Controller
339
Chapter 12 Book Details View Controller
let iconButton:UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
b utton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 5,
bottom: 0, right: 0)
button.contentHorizontalAlignment = .left
button.tintColor = .black
button.clipsToBounds = true
button.isEnabled = false
return button
}()
/*
An image view to show the Book Field image
*/
imageView.tintColor = .blue
return imageView
}()
/*
A label to show the book field data
*/
(continued)
340
Chapter 12 Book Details View Controller
let iconLabelButton:UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
b utton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 5,
bottom: 2, right: 0)
button.contentHorizontalAlignment = .left
button.contentVerticalAlignment = .center
button.setTitleColor(.black, for: .normal)
b utton.titleLabel?.font = UIFont(name: "Helvetica",
size: 14)
button.clipsToBounds = true
return button
}()
341
Chapter 12 Book Details View Controller
contentView.addSubview(iconButton)
c ommonFunctions.setFieldLayout(mainField: iconButton,
constraintField: contentView, topAnchor: 2, leftAnchor: 2,
rightAnchor: -345, heightAnchor: 20)
contentView.addSubview(leftImageView)
c ommonFunctions.setFieldLayout(mainField: leftImageView,
constraintField: contentView, topAnchor: 6, leftAnchor: 2,
rightAnchor: -360, heightAnchor: 20)
contentView.addSubview(iconLabelButton)
c ommonFunctions.setFieldLayout(mainField: iconLabelButton,
constraintField: contentView, topAnchor: 2, leftAnchor:
20, rightAnchor: 0, heightAnchor: 30)
342
Chapter 12 Book Details View Controller
Summary
This chapter was not introduced in the Book Tracker Basic App (Part I).
In this chapter, we learned how to display details content of a book (Book
Name, Author Name, Book Genre, Reading Status) and provide a list of
actions (Share the Book, Edit Book Details, Delete the Book, Take Book
Notes, Set Book Reminder, and mark it as Favorite).
The next six chapters are dedicated to each of the actions we have on
the Book Detail View Controller.
343
CHAPTER 13
Sharing Book
with Other Users
One of the functionalities iCloud offers is sharing content with other users.
In the CloudKit Overview section, a brief outline of the share concept was
discussed. In this section, we will learn the actual implementation, where
we will share a book with another user of the Book Tracker App. The App
had a screen dedicated for Shared books where all shared books by other
users are displayed, as shown in Figure 13-1.
Before we can see the books in the Shared Books, we need to first learn
how to share a book. Our Book Detail screen first option block is Share, as
shown in Figure 13-2. This section we will learn how to share a book. The
Shared Books Display we will learn in the next chapter.
346
Chapter 13 Sharing Book with Other Users
Import CloudKit
Importing the CloudKit toolkit is required to get access to all native iCloud
functions, as shown in Table 13-1.
347
Chapter 13 Sharing Book with Other Users
348
Chapter 13 Sharing Book with Other Users
2. animation(sender: sender)
animation will help to show a bounce effect. We will learn about it later.
3. self.shareRecord()
This method will help us to share the record. We will learn this method in the next
section.
4. self.databaseFunctions.group.enter()
5. self.databaseFunctions.group.notify(queue: .main) {
6. }
This is an await block for the share block to finish the work
7. }
349
Chapter 13 Sharing Book with Other Users
350
Chapter 13 Sharing Book with Other Users
share = CKShare(rootRecord:
recordToShare!)
share![CKShare.SystemFieldKey.title] =
"Sharing Book" as CKRecordValue?
share![CKShare.SystemFieldKey.
shareType] = "Book" as CKRecordValue
let shareController =
UICloudSharingController {
shareController,
preparationCompletionHandler in
let modRecordsOp = CKModifyRe
cordsOperation(recordsToSave:
[recordToShare!, share!],
recordIDsToDelete: nil)
modRecordsOp.modifyRecords
CompletionBlock = { records,
recordIDs, error in
if error != nil {
print("modifyRecordsOp
error:", error!)
}
preparationCompletionHandler(
share, CKContainer.default(),
error)
}
privateDB.add(modRecordsOp)
}
(continued)
351
Chapter 13 Sharing Book with Other Users
shareController.delegate = self
shareController.availablePermissions =
[.allowPrivate, .allowReadWrite]
shareController.
popoverPresentationController?.
sourceView = self.shareImageView
self.presentationController?.
presentedViewController.
present(shareController, animated:
true, completion: nil)
}
}
}
self.databaseFunctions.group.leave()
}
}
1. func shareRecord(){
Custom function called from Share Block button touch up inside event.
2. DispatchQueue.main.async {
We are creating a queue to help perform the share record execution to happen
asynchronously
We will define a local method variable called share of type CKShare. This will show
the Record Share Screen to the user.
4. let privateDB = CKContainer.default().privateCloudDatabase
5. var pred = NSPredicate(value: true)
(continued)
352
Chapter 13 Sharing Book with Other Users
6. print(self.book.bookname!)
7. print(self.book.authorname!)
8. p red = NSPredicate(format: "bookname == %@ && authorname
== %@", self.book.bookname, self.book.authorname)
9. let query = CKQuery(recordType: "Book", predicate: pred)
10. let operation = CKQueryOperation(query: query)
11. let ckRecordZoneID = CKRecordZone(zoneName: "SharedZone")
12. operation.zoneID = ckRecordZoneID.zoneID
13. CKContainer.default().privateCloudDatabase.add(operation)
14. var recordToShare:CKRecord?
15. operation.recordFetchedBlock = { record in
16. recordToShare = record
17. }
The book we are going to share is currently in the user’s private Database. We are
using the preceding methods to get access to the database and search for the book
that needs to be shared. Once we query and locate the record, we will store thar
record in a CKRecord variable type called recordToShare
As this is an asynchronous call, once the query is completed, it will return to this
block of code
19. DispatchQueue.main.async {
20. if let error = error {
21. print(error)
353
Chapter 13 Sharing Book with Other Users
The preceding line of code is making the book record we queried to a Shared
Record type
This is a prompt which will be displayed on the shared popup screen. The title of the
screen – we are naming it “Sharing Book”
Before sharing, we need to inform the iCloud database about the record type. Our
book is of type Book defined in the iCloud Database.
We are creating a Sharing Controller screen which will be shown to the user. Also
note, we need to mark this record in the Private Database as shared. When the user
accepts the request, it will be then stored in a shared database.
30.modRecordsOp.modifyRecordsCompletionBlock = { records,
recordIDs, error in
31. if error != nil {
(continued)
354
Chapter 13 Sharing Book with Other Users
The preceding line of code commits the book record to the database
42. }
43. }
44. }
45. self.databaseFunctions.group.leave()
Once the sharing is done, we will leave the queue and return the control to the
waiting block.
46. }
47. }
355
Chapter 13 Sharing Book with Other Users
*/
356
Chapter 13 Sharing Book with Other Users
Summary
Sharing the book with another Book Tracker App user is a cool feature. In
this chapter, we learned how to Share records securely amongst users. We
also learned the concepts of Private and shared iCloud databases in this
chapter.
In the next chapter, we will learn how to Edit the Details of a book.
357
CHAPTER 14
Edit Book
In the Book detail screen, the Edit Book block is the second block next to
the Share Book block. This block of functions helps in modifying the book
attributes (Book Name, Author Name, Genre, and Status). While defining the
Edit Book button, a custom function was assigned on the Touch Up Inside
Event called “completeButtonAction”. When the user taps on the block code
shown in Table 14-1 the Touch Up Inside function is invoked which opens
the Edit Book View Controller.
360
Chapter 14 Edit Book
This variable holds the book details which is passed by the calling function from the
Edit Book Block of Book Details View Controller. The update book details will also be
stored in this class variable
3.
var delegateBookViewRefresh:RefreshBooksViewProtocol? = nil
This is the reference to delegate. Once the update is done and the screen is
dismissed, we need to update the Book Detail view with the updates. Refer to
Custom Delegation section to learn more.
Navigation Bar
There is no change in the Navigation Bar definition except for the Title of
the Bar, as shown in Table 14-3.
361
Chapter 14 Edit Book
navigationBar.translatesAutoresizingMaskIntoConstraints = false
n avigationBar.backgroundColor = UIColor.init(red: 171/255,
green: 189/255, blue: 217/255, alpha: 1)
l et resetSymbolConfiguration = UIImage.
SymbolConfiguration(pointSize: 15, weight: .black)
l et saveSymbolConfiguration = UIImage.
SymbolConfiguration(pointSize: 20, weight: .black)
362
Chapter 14 Edit Book
Setup
The setup is the same as defined in the Add View Controller. The only
change is assignment of the book fields with the values passed to the class
(Highlighted section). Note, we need to show user what they need to edit,
as shown in Table 14-4.
genreTableView.delegate = self
genreTableView.dataSource = self
g enreTableView.register(UITableViewCell.self,
forCellReuseIdentifier: "genre")
genreTableView.layer.borderWidth = 0
genreTableView.layer.borderColor = UIColor.white.cgColor
genreTableView.separatorStyle = .none
statusTableView.delegate = self
statusTableView.dataSource = self
s tatusTableView.register(UITableViewCell.self,
forCellReuseIdentifier: "status")
statusTableView.layer.borderWidth = 0
statusTableView.layer.borderColor = UIColor.white.cgColor
statusTableView.separatorStyle = .none
commonVariables.setGenres()
commonVariables.setStatuses()
bookTextField.text = book.bookname
authorTextField.text = book.authorname
genreTextField.text = book.genre
statusTextField.text = book.status
(continued)
363
Chapter 14 Edit Book
originalBook.bookname = book.bookname
originalBook.authorname = book.authorname
}
Save Book
The only change in the save book project is calling the Update Book
Database functions instead of Add Book (highlighted Section). Also, the
delegate calls to refresh the data on the Book Detail view, as shown in
Table 14-5.
364
Chapter 14 Edit Book
s elf.delegateBookViewRefresh?.refreshData(book: self.
book)
self.dismiss(animated: true, completion: nil)
}
365
Chapter 14 Edit Book
l et sharedDatabase = CKContainer.default().
privateCloudDatabase
CKContainer.default().privateCloudDatabase.add(operation)
operation.recordFetchedBlock = { record in
record["bookname"] = book.bookname
record["authorname"] = book.authorname
record["genre"] = book.genre
record["status"] = book.status
s haredDatabase.save(record, completionHandler: {
(record, error) -> Void in
DispatchQueue.main.async {
if let error = error {
print(error)
}
else {
self.syncQueue.async {
(continued)
366
Chapter 14 Edit Book
Summary
In this chapter, we learned how to change the Book Details (Book Name
and Author Name) and select a different value for Genre and Book Reading
Status. We also learned how to update a book record in iCloud database.
In the next chapter, we will learn to delete a book from iCloud
repository.
367
CHAPTER 15
Book Delete
The third option on the Block Menu is deleting a Book. When a user clicks
on the Delete Block, “deleteButtonAction” custom function is invoked,
which provides an alert to the user asking for his/her confirmation to
delete the book. If users say “YES” the book is deleted.
Following is the progress made so far on Book Detail Screen:
• Book Notes
The button Touch Up Inside Event defined for the delete block during
the view definition
2. animation(sender: sender)
animation will help to show a bounce effect. We will learn about it later.
3. deleteBook()
4. }
370
Chapter 15 Book Delete
371
Chapter 15 Book Delete
/*
d eleteBook function deletes a book from the repository based
on the book name and author name
*/
372
Chapter 15 Book Delete
373
Chapter 15 Book Delete
Summary
In this chapter, we learned how to permanently delete a record from the
iCloud repository. In the next chapter, we will learn how to take additional
notes on the book.
374
CHAPTER 16
Book Notes
In the Book Detail View, the Book Notes allows users to take multiple notes
on the book. The notes are displayed on a new screen. Users can add and
delete notes from the popup screen. Figure 16-1 provides the screen layout
of the Book Notes screen.
• Book Notes
/*
W hen the user clicks on the Book Notes Button the following
function will be triggered. It will do the following:
viewController.modalTransitionStyle = .crossDissolve
s elf.present(viewController, animated: true, completion:
nil)
}
The function pops up the book Notes screen. It takes the book record as a reference
so that it knows which book it must store the notes for.
377
Chapter 16 Book Notes
•
Book Notes variable of type Booknote (defined in Database function class –
see below)
378
Chapter 16 Book Notes
/*
b ooknotes is an array variable of type Booknote. booknote is
a variable of type Booknote
*/
var booknotes = [Booknote]()
var booknote = Booknote()
Booknote Class has notes as a String variable to store notes. It also has reference
to the Book and the author’s name and the unique Record id of the book for which
the note is stored.
The booknotes is a local array defined to store all booknotes(for query purposes)
and a booknote variable to save the current book note.
379
Chapter 16 Book Notes
/*
Following UI Object will be used to draw the screen:
- a Text View to take free flow text as notes
- a largepopup to cover the Book Notes screen
- a popup which will show the Text view and a navigation bar
to save the book note when Add notes (the first row of the
Book Note table) option is clicked
- A Navigation bar with two buttons - one to close the popup
without saving and the other button to save and close the
book note
- A Tab bar with a button to close the screen
- A Book Notes table view to show the avaiable book notes
*/
(continued)
380
Chapter 16 Book Notes
return textView
}()
private let largerPopUpView:UIView = {
let uiView = UIView()
uiView.translatesAutoresizingMaskIntoConstraints = false
uiView.backgroundColor = .white
uiView.layer.borderColor = UIColor.black.cgColor
uiView.layer.borderWidth = 1
uiView.layer.cornerRadius = 5
return uiView
}()
381
Chapter 16 Book Notes
l et cancelSymbolConfiguration = UIImage.
SymbolConfiguration(pointSize: 25, weight: .black)
l et cancelImage = UIImage(systemName: "arrowshape.turn.
up.left.fill", withConfiguration: cancelSymbolConfiguration)
l et cancelItem = UIBarButtonItem(image: cancelImage,
landscapeImagePhone: cancelImage, style: .plain, target:
nil, action: #selector(notesCancelButtonAction))
navItem.leftBarButtonItem = cancelItem
l et saveSymbolConfiguration = UIImage.
SymbolConfiguration(pointSize: 25, weight: .black)
l et saveImage = UIImage(systemName: "s.circle.fill",
withConfiguration: saveSymbolConfiguration)
l et saveItem = UIBarButtonItem(image: saveImage,
landscapeImagePhone: saveImage, style: .plain, target: nil,
action: #selector(notesSaveActionButton))
navItem.rightBarButtonItem = saveItem
navigationBar.setItems([navItem], animated: false)
return navigationBar
}()
(continued)
382
Chapter 16 Book Notes
Initial Loading
We are going to use the default viewDidLoad function to set up the view
Controller and a function bookNotesQuery to query the Booknote table
to display all available book notes for the book. Table 16-6 has the relevant
code details.
383
Chapter 16 Book Notes
/*
L everaging the default viewDidLoad methos to initiate
a custom function setup to perform initial setup and a
bookNotesQuery function to query all the available book
notes for the book
*/
/*
The setup function does the following:
- set the background color of the view to white
- set the tab bar delegate
- set the book notes table view. The book note table view
uses a custom cell NotesTableViewCell
*/
func setup(){
view.backgroundColor = .white
tabBar.delegate = self
bookNotesTableView.delegate = self
bookNotesTableView.dataSource = self
b ookNotesTableView.register(NotesTableViewCell.self,
forCellReuseIdentifier: "note")
(continued)
384
Chapter 16 Book Notes
bookNotesTableView.layer.borderWidth = 0
b ookNotesTableView.layer.borderColor = UIColor.white.
cgColor
bookNotesTableView.separatorStyle = .none
}
/*
T he bookNotesQuery queries the repository for all available
booknotes for the current book. Once the query completes it
stores all the booknotes in a local variable "booknotes" and
draws the screen
*/
fileprivate func bookNotesQuery(){
self.databaseFunctions.booknotes.removeAll()
databaseFunctions.group.enter()
databaseFunctions.bookNotesQuery(book: book)
databaseFunctions.group.notify(queue: .main) {
self.booknotes = self.databaseFunctions.booknotes
self.drawScreen()
}
}
The Setup function sets the background color to white. Set delegates for Tab
Bar and the Book Notes Table View. Please note we are using a custom cell
NotesTableViewCell for displaying the notes.
The query function queries the Book Notes Table and once the query is completed,
it stores all book notes in a local booknotes variable of type Booknote class and
draws the screen with the UI Object.
385
Chapter 16 Book Notes
import UIKit
/*
V ariable to access the CommonFunctions class to draw the UI
Object in cell
*/
/*
An UI Button defined which will be drawn in the cell
*/
let notesLabelButton:UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
b utton.contentEdgeInsets = UIEdgeInsets(top: 0, left: 5,
bottom: 2, right: 0)
(continued)
386
Chapter 16 Book Notes
button.contentHorizontalAlignment = .left
button.contentVerticalAlignment = .center
button.setTitleColor(.black, for: .normal)
b utton.titleLabel?.font = UIFont(name: "Helvetica",
size: 14)
button.clipsToBounds = true
button.layer.borderColor = UIColor.lightGray.cgColor
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.titleLabel?.numberOfLines = 4
return button
}()
/*
T his function is called every time the table cell is drawn.
The function draws the custom UI Button in the cell
*/
387
Chapter 16 Book Notes
/*
T his function draws the Book Notes screen. If Book notes are
avaiable it will display the same. The First row is always
Add Notes
*/
func drawScreen(){
let screensize: CGRect = UIScreen.main.bounds
let screenHeight = screensize.height
view.addSubview(bookNotesTableView)
c ommonFunctions.setFieldLayout(mainField:
bookNotesTableView, constraintField: view!, topAnchor:
50, leftAnchor: 5, rightAnchor: -5, heightAnchor:
screenHeight - 100)
view.addSubview(tabBar)
c ommonFunctions.setFieldLayout(mainField: tabBar,
constraintField: view!, topAnchor: screenHeight - 80,
leftAnchor: 0, rightAnchor: 0, heightAnchor: 60)
tabBar.unselectedItemTintColor = .blue
}
(continued)
388
Chapter 16 Book Notes
389
Chapter 16 Book Notes
/*
numberOfRowsInSection will let the table know how many rows
are in each section.
*/
f unc tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return self.booknotes.count
}
/*
c ellForRowAt will display the records. The First row is Add
Notes option and it gets a Blue text color to make it stand out.
*/
390
Chapter 16 Book Notes
l et cell = tableView.dequeueReusableCell(withIdentifier:
"note", for: indexPath) as! NotesTableViewCell
c ell.notesLabelButton.addTarget(self, action:
#selector(iconButtonAction), for: .touchUpInside)
if(indexPath.row == 0){
cell.notesLabelButton.setTitleColor(.blue, for: .normal)
cell.notesLabelButton.layer.borderWidth = 0
}else{
c ell.notesLabelButton.setTitleColor(.black, for:
.normal)
cell.notesLabelButton.layer.borderWidth = 1
}
c ell.notesLabelButton.setTitle(self.booknotes[indexPath.
row].notes, for: .normal)
cell.notesLabelButton.tag = indexPath.row
c ell.notesLabelButton.accessibilityIdentifier =
String(indexPath.section)
return cell
}
/*
The height of the each row is set to 85 pixel
*/
391
Chapter 16 Book Notes
headerView.addSubview(label)
return headerView
}
/*
T he height of the each header section is set to the default
40 pixel
*/
392
Chapter 16 Book Notes
/*
T he trailingSwipeActionsConfigurationForRowAt will display
a Trash icon upon left swipe, which will allow to delete an
individual book note
*/
f unc tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath:
IndexPath) -> UISwipeActionsConfiguration? {
s elf.databaseFunctions.deleteBookNote(notetoDelete:
self.booknotes[indexPath.row].notes,bookname: self.
booknotes[indexPath.row].bookname)
self.databaseFunctions.group.notify(queue: .main) {
self.booknotes.remove(at: indexPath.row)
self.bookNotesTableView.reloadData()
}
}
l et cancelAction = UIAlertAction(title: "Cancel",
style: UIAlertAction.Style.default) {
UIAlertAction in
(continued)
393
Chapter 16 Book Notes
})
deleteAction.backgroundColor = .red
deleteAction.image = UIImage(systemName: "trash")
r eturn UISwipeActionsConfiguration(actions:
[deleteAction])
}
This part we have learned before. If you need to revisit this section, please refer to
previous table view definitions.
Please note the first row of the Book Notes will be displayed as an “Add Note”
button. This will provide users with an option to add a note to the Book. To enable
the “Add Note” to popup a view to add a note, we are adding a method on the Touch
Up Inside event called “iconButtonAction”. Refer the preceding lines of code for
the implementation details.
394
Chapter 16 Book Notes
l et deleteAction = UIContextualAction(style:
.destructive, title: "Delete", handler: { (action, view,
success) in
395
Chapter 16 Book Notes
UIAlertAction in
self.bookNotesTableView.reloadData()
}
})
deleteAction.backgroundColor = .red
deleteAction.image = UIImage(systemName: "trash")
r eturn UISwipeActionsConfiguration(actions:
[deleteAction])
1.
func tableView(_ tableView: UITableView,
trailingSwipeActionsConfigurationForRowAt indexPath:
IndexPath) -> UISwipeActionsConfiguration? {
This is the system-defined table view function that allows swipe gestures on table
cells. Upon swipe, the table view provides option(s) for users to act. In this case, we
will show a delete note option.
(continued)
396
Chapter 16 Book Notes
2.
let deleteAction = UIContextualAction(style: .destructive,
title: "Delete", handler: { (action, view, success) in
For our Book Note swipe, we are defining a delete action – giving it a title and a
style (destructive means if we swipe completely the action will be performed)
6. UIAlertAction in
7.
self.databaseFunctions.group.enter()
8.
self.databaseFunctions.deleteBookNote(notetoDelete:
self.booknotes[indexPath.row].notes,bookname: self.
booknotes[indexPath.row].bookname)
9. self.databaseFunctions.group.notify(queue: .main) {
11. self.bookNotesTableView.reloadData()
When the user performs the delete action by swiping on a book note, an alert is
shown to confirm deletion, and if user confirms to the action a DatabaseFunctions
method deleteBookNote is called to perform the delete note action (details on the
method is provided in the next section)
12. }
13. }
(continued)
397
Chapter 16 Book Notes
15. UIAlertAction in
16. self.bookNotesTableView.reloadData()
If user decided not to delete, it refreshes the table and closes the alert.
17. }
19. alertController.addAction(cancelAction)
20. alertController.addAction(okAction)
Adding the OK and CANCEL buttons to the alert Controller. The alert Controller is
then presented using the system-defined present method.
23. })
398
Chapter 16 Book Notes
Delete Notes
This is a database function method used for deleting the note. Add the
method shown in Table 16-11 to the DatabaseFunctions class.
func deleteBookNotes(bookname:String){
var taskRecords = [CKRecord.ID]()
let pred = NSPredicate(format: "bookname = %@",bookname)
l et query = CKQuery(recordType: "Booknotes", predicate:
pred)
let operation = CKQueryOperation(query: query)
let ckRecordZoneID = CKRecordZone(zoneName: "SharedZone")
operation.zoneID = ckRecordZoneID.zoneID
CKContainer.default().privateCloudDatabase.add(operation)
operation.recordFetchedBlock = { record in
taskRecords.append(record.recordID)
}
operation.queryCompletionBlock = { (cursor, error) in
DispatchQueue.main.async {
l et modifyRecordsOperation = CKModifyRecordsOperation.
init(recordsToSave: nil, recordIDsToDelete: taskRecords)
m odifyRecordsOperation.modifyRecordsCompletionBlock = {
records, recordIDs, error in DispatchQueue.main.async {
(continued)
399
Chapter 16 Book Notes
C KContainer.default().privateCloudDatabase.
add(modifyRecordsOperation)
}}
}
We have learned how to delete a record from an iCloud table before. Please refer to
the previous section for the same. Please note the table is Booknotes (which stores
all book notes) and we are only deleting the book note on which delete swipe action
was performed.
Adding Notes
When user clicks the “Add Note” option, the event shown in Table 16-12 is
triggered.
400
Chapter 16 Book Notes
Table 16-12. Add Note Custom Function
/*
When Add Notes is clicked (the first row) it will will draw
the Book Notes Input Screeen
*/
401
Chapter 16 Book Notes
view.addSubview(largerPopUpView)
c ommonFunctions.setFieldLayout(mainField: largerPopUpView,
constraintField: view!, topAnchor: 0, leftAnchor: 0,
rightAnchor: 0, heightAnchor: screenHeight)
l argerPopUpView.backgroundColor = UIColor(displayP3Red:
50, green: 50, blue: 50, alpha: 0.7)
largerPopUpView.addSubview(popUpView)
c ommonFunctions.setFieldLayout(mainField: popUpView,
constraintField: view!, topAnchor: 100, leftAnchor: 20,
rightAnchor: -20, heightAnchor: 350)
p opUpView.layer.shadowPath = UIBezierPath(roundedRect:
CGRect(x: -10, y: -10, width: 370, height: 370),
cornerRadius: 12).cgPath
popUpView.layer.shadowRadius = 2
popUpView.layer.shadowOffset = .zero
popUpView.layer.shadowOpacity = 0.8
(continued)
402
Chapter 16 Book Notes
popUpView.layer.shouldRasterize = true
view.addSubview(createNotesNavigationBar)
c ommonFunctions.setFieldLayout(mainField:
createNotesNavigationBar, constraintField: popUpView,
topAnchor: 0, leftAnchor: 0, rightAnchor: 0,
heightAnchor: 20)
view.addSubview(bookTextView)
c ommonFunctions.setFieldLayout(mainField: bookTextView,
constraintField: createNotesNavigationBar, topAnchor: 50,
leftAnchor: 2, rightAnchor: -2, heightAnchor: 290)
bookTextView.becomeFirstResponder()
}
The screen will have a navigation bar and a text view. Navigation bar provides two
buttons: one to save the book note, and the other button will allow the user to close
the popup view. The Text view will be used to take book notes input from the user.
403
Chapter 16 Book Notes
404
Chapter 16 Book Notes
/*
The navigation bar save button to save the book note
*/
@objc func notesSaveActionButton(sender: UIButton){
saveNotes()
}
When the cancel button is selected, we remove all UI Objects from the popup
screen. As the book note can be displayed again, it is a good practice to remove all
UI Objects to avoid any constraint conflicts while drawing the UI Objects.
If Save action is selected, we will call the custom Save Notes method to save the
note to iCloud repository.
Remove Constraints
The code in Table 16-15 provides the detail on removing the Book Notes
screen when users close the popup book notes screen.
405
Chapter 16 Book Notes
func removeAddNotesPopUpConstraints(){
l argerPopUpView.removeConstraints(largerPopUpView.
constraints)
largerPopUpView.removeFromSuperview()
popUpView.removeConstraints(popUpView.constraints)
popUpView.removeFromSuperview()
c reateNotesNavigationBar.removeConstraints(createNotesNavi
gationBar.constraints)
createNotesNavigationBar.removeFromSuperview()
bookTextView.removeConstraints(bookTextView.constraints)
bookTextView.removeFromSuperview()
}
Function to remove all the popup view UI Objects so that we can revert to the main
display.
406
Chapter 16 Book Notes
func saveNotes(){
if(bookTextView.text == ""){
bookTextView.text = ""
l et alertController = UIAlertController(title: "Save
Alert", message: "No Notes to Save", preferredStyle:
.alert)
// Create the actions
l et okAction = UIAlertAction(title: "OK", style:
UIAlertAction.Style.default) {
UIAlertAction in
self.tabBarController?.selectedIndex = 0
}
self.databaseFunctions.group.enter()
self.databaseFunctions.saveBookNotes()
self.databaseFunctions.group.notify(queue: .main) {
self.removeAddNotesPopUpConstraints()
self.bookTextView.text = ""
self.bookNotesTableView.reloadData()
}
}
}
This function will save notes after doing all validation to the “Booknotes” Record
type in the iCloud database repository.
func saveBookNotes(){
let ckRecordZoneID = CKRecordZone(zoneName: "SharedZone")
(continued)
408
Chapter 16 Book Notes
aRecord["bookname"] = booknote.bookname
aRecord["authorname"] = booknote.authorname
aRecord["booknotes"] = booknote.notes
p rivateDatabase.save(aRecord, completionHandler: {
(record, error) -> Void in
DispatchQueue.main.async {
(continued)
409
Chapter 16 Book Notes
/*
D ismissing the Book Notes screen and return to the Book
Details screen
*/
410
Chapter 16 Book Notes
Summary
In this chapter, we learned how to set up parent child records in the iCloud
database. One book can have many notes. We learned how to Add notes,
delete notes, cascading delete all book notes when a book is deleted.
Next chapter, we will learn how to set up a Book Reminder.
411
CHAPTER 17
Book Reminder
Think of a scenario, you are putting together a list of books you want to
read on your next vacation, and now that you have a list of books you want
to read, you now want to set a reminder on the day of your vacation so that
you can carry the book and eventually read it.
When we set a reminder for a book, it will show on the iPhone
notification center as shown in Figure 17-2. Please note that for accessing
the notification, the user must give permission to the App to show the
notification as shown in Figure 17-1.
Following is the progress made so far on Book Detail Screen:
414
Chapter 17 Book Reminder
415
Chapter 17 Book Reminder
• A Date and time scroll for user to set a date and a time
when the reminder should appear for the book
• Two image views. The first one will save the reminder
to the iCloud repository and the second image view
will reset the reminder to a back date. Image view does
not have the touchUpInside event. To enable the click
event, we need to define it as a tap gesture event.
func drawReminderPopupScreen(){
let screensize: CGRect = UIScreen.main.bounds
let screenHeight = screensize.height
view.addSubview(largerPopUpView)
commonFunctions.setFieldLayout(mainField: largerPopUpView,
constraintField: view!, topAnchor: 0, leftAnchor: 0,
rightAnchor: 0, heightAnchor: screenHeight)
largerPopUpView.backgroundColor = UIColor(displayP3Red:
50, green: 50, blue: 50, alpha: 0.7)
largerPopUpView.addSubview(popUpView)
commonFunctions.setFieldLayout(mainField: popUpView,
constraintField: view!, topAnchor: 300, leftAnchor:
40, rightAnchor: -40, heightAnchor: 200)
(continued)
416
Chapter 17 Book Reminder
popUpView.layer.shadowPath = UIBezierPath(roundedRect:
CGRect(x: -10, y: -10, width: 330, height: 220),
cornerRadius: 12).cgPath
popUpView.layer.shadowRadius = 2
popUpView.layer.shadowOffset = .zero
popUpView.layer.shadowOpacity = 0.8
popUpView.layer.shadowColor = UIColor.gray.cgColor
popUpView.layer.shouldRasterize = true
popUpView.addSubview(reminderLabel)
commonFunctions.setFieldLayout(mainField:
reminderLabel, constraintField: popUpView, topAnchor:
10, leftAnchor: 2, rightAnchor: -2, heightAnchor: 40)
reminderLabel.text = "Set Reminder for Book \"\(book.
bookname.capitalized)\""
reminderLabel.font = UIFont.boldSystemFont(ofSize: 16)
popUpView.addSubview(reminderdatePicker)
commonFunctions.setFieldLayout(mainField:
reminderdatePicker, constraintField: popUpView, topAnchor:
80, leftAnchor: 0, rightAnchor: -40, heightAnchor: 40)
if(book.reminder != nil){
reminderdatePicker.date = book.reminder
}
popUpView.addSubview(reminderPopUpImageView)
commonFunctions.setFieldLayout(mainField: reminder
PopUpImageView, constraintField: popUpView, topAnchor:
140, leftAnchor: 100, rightAnchor: -160, heightAnchor: 40)
popUpView.addSubview(reminderResetImageView)
commonFunctions.setFieldLayout(mainField: reminderReset
ImageView, constraintField: popUpView, topAnchor: 140,
leftAnchor: 170, rightAnchor: -90, heightAnchor: 40)
}
417
Chapter 17 Book Reminder
Save Reminder
As Image View doesn’t have any default event defined, we need to add a
tap gesture (see setup section to understand how to define tap gesture).
The function does the following:
418
Chapter 17 Book Reminder
• Table 17-3 has the relevant code details for the tap
gesture response on image view.
Setup Reminder
This method sets the notification center with the book name that needs
to be reminded of the set date and time. Table 17-4 has the relevant code
details.
419
Chapter 17 Book Reminder
420
Chapter 17 Book Reminder
center.getNotificationSettings { settings in
if settings.authorizationStatus == .authorized {
addRequest()
} else {
center.requestAuthorization(options: [.alert,
.badge, .sound]) { success, error in
if success {
addRequest()
} else {
print("Error is \(error?.localized
Description ?? "No error Code")")
}
}
}
}
}
(continued)
421
Chapter 17 Book Reminder
422
Chapter 17 Book Reminder
423
Chapter 17 Book Reminder
Reset Reminder
As Imageview does not have any event attached to it, we need to add a tap
gesture (see setup section to understand how to define tap gesture).
The function removed all UI Objects from the reminder popup screen,
as shown in Table 17-5.
424
Chapter 17 Book Reminder
Update Reminder
The method defined in Table 17-6 calls the DatabaseFunctions Update
Reminder Method to save the reminder data in Book Record Type. After
saving, it removes all UI Objects of the Reminder Popup screen.
425
Chapter 17 Book Reminder
func updateBookReminder(){
self.databaseFunctions.group.enter()
self.databaseFunctions.updateBookReminder(orginalBook:
book)
self.databaseFunctions.group.notify(queue: .main) {
self.removeReminderContraints()
}
}
426
Chapter 17 Book Reminder
/*
When reminder is set updateBookReminder updates the book
record with the reminder date
*/
record["reminder"] = orginalBook.reminder
sharedDatabase.save(record, completionHandler:
{ (record, error) -> Void in
DispatchQueue.main.async {
if let error = error {
print(error)
}
(continued)
427
Chapter 17 Book Reminder
else {
self.syncQueue.async {
let book = Book()
book.bookname = record!.
object(forKey: "bookname")! as?
String
book.authorname = record!.
object(forKey: "authorname")!
as? String
book.genre = record!.
object(forKey: "genre")! as?
String
book.status = record!.
object(forKey: "status")! as?
String
self.book = book
self.group.leave()
}
}
}
})
}
}
428
Chapter 17 Book Reminder
Summary
In this chapter, we learned how to use the default iOS Notification center to
pop up reminders on books (example, you want to set a reminder to read a
book) on a particular date and time.
The next chapter will conclude the Book Detail screen. We will learn
how to make a Book Favorite.
429
CHAPTER 18
Mark Favorite
In the main display screen, we have a section dedicated to Favorite books.
Any book that is marked as favorite is shown in that section, regardless of
the Book Status (Read/Reading/To be Read).
• Book Notes
432
Chapter 18 Mark Favorite
self.databaseFunctions.updateBookFavorite(orginalBook:
book)
self.databaseFunctions.group.notify(queue: .main) {
let alertController = UIAlertController(title:
"Favorite Alert", message: "Book '\(self.book.
bookname!)' is Saved", preferredStyle: .alert)
433
Chapter 18 Mark Favorite
2. if(book.favorite == 0){
3. book.favorite = 1
4. favButton.backgroundColor = .lightGray
5. favTextLabel.backgroundColor = .lightGray
6. }else{
7. book.favorite = 0
8. favButton.backgroundColor = .white
9. favTextLabel.backgroundColor = .white
10. }
In the Book Record Type for the field favorite, we store 1 for book marked as
favorite, otherwise the field will store 0. Line 8–16 is checking for the favorite
status and making the background gray if it is marked as a favorite book,
otherwise the background color is set to white.
11. self.databaseFunctions.group.enter()
12. self.databaseFunctions.updateBookFavorite(orginalBook: book)
13. self.databaseFunctions.group.notify(queue: .main) {
1 4. let alertController = UIAlertController(title: "Favorite
Alert", message: "Book '\(self.book.bookname!)' is Saved",
preferredStyle: .alert)
15. // Create the actions
(continued)
434
Chapter 18 Mark Favorite
435
Chapter 18 Mark Favorite
436
Chapter 18 Mark Favorite
437
Chapter 18 Mark Favorite
Animation Function
The Animation function helps to give the currently tapped block a
bouncing effect. The code detailed in Table 18-3 has the relevant details.
438
Chapter 18 Mark Favorite
Custom function to perform the bouncing effect. It takes Button that need to get the
bounce affect as an input parameter
3. UIView.animate(withDuration: 2.0,
4. delay: 0,
5. usingSpringWithDamping: CGFloat(0.20),
6. initialSpringVelocity: CGFloat(6.0),
7. options: UIView.AnimationOptions.allowUserInteraction,
8. animations: {
9. sender.transform = CGAffineTransform.identity
10. },
11. completion: { Void in() }
12. )
On the UI View, we are invoking the animation function. It takes three parameters –
time duration, any delay that we need to define for the animation, and the object to
animate.
13. }
439
Chapter 18 Mark Favorite
Summary
In this chapter, we learned how to set up a book as Favorite. The books
marked as Favorite show up in the display screen under the Favorite
section, giving easy access to users’ favorite books.
This concludes the Book Detail Section of the book. In the next
chapter, we will learn how to display the shared books from other users in
the Shared Tab of the Book Tracker App.
440
CHAPTER 19
acceptSharing.qualityOfService = .userInteractive
acceptSharing.perShareCompletionBlock = {meta, share,
error in
print("successfully shared")
}
acceptSharing.acceptSharesCompletionBlock = {
error in
guard (error == nil) else{
print(error!)
print("Error XX \(error?.localizedDescription
?? "No Error Code to Show")")
return
}
viewController.fetchShare(cloudKitShareMetadata)
}
CKContainer(identifier: cloudKitShareMetadata.
containerIdentifier).add(acceptSharing)
}
442
Chapter 19 Shared Books Tab
443
Chapter 19 Shared Books Tab
10. acceptSharing.acceptSharesCompletionBlock = {
11. error in
12. guard (error == nil) else{
13. print(error!)
14. print("Error XX \(error?.localizedDescription ?? "No
Error Code to Show")")
15. return
16. }
This block simply accepts the record and prints if there are any errors
17. viewController.fetchShare(cloudKitShareMetadata)
If there are no errors in accepting the shared record, in that case we are calling
our custom Fetch Share method defined in the BooksViewController. The
custom method takes CloudKit shared metadata as input. This variable will have
the reference to the shared record
18. }
19. CKContainer(identifier: cloudKitShareMetadata.
containerIdentifier).add(acceptSharing)
This line of code will inform the CloudKit container that are accepting the shared
record. Please note, this will only happen when the recipient user accepts the
Shared Record.
20. }
444
Chapter 19 Shared Books Tab
/*
This function is called when a book share is accepted by
a user. We will invoke this from Scene Delegate system
class. The function will retrieve the shared record and
store in User Shared Database in iCloud.
*/
record?.setValue(Int64(cloudKitShareMetada
ta.participantPermission.rawValue), forKey:
"sharepremission")
445
Chapter 19 Shared Books Tab
alert.addAction(UIAlertAction(title: "OK",
style: .default, handler: { action in
switch action.style{
case .default:
print("default")
case .cancel:
print("cancel")
case .destructive:
print("destructive")
@unknown default:
print("error")
}}))
self.present(alert, animated: true, completion:
nil)
//self.querySharedTasks()
446
Chapter 19 Shared Books Tab
447
Chapter 19 Shared Books Tab
448
Chapter 19 Shared Books Tab
449
Chapter 19 Shared Books Tab
/*
Before doing any query on Shared Databases we need to
retrieve all zones that the other users have shared. The
sharedTasksZones function retrieves all such zone and
stores it in a class level variable sharedRecordZones
*/
450
Chapter 19 Shared Books Tab
Query Functions
This function will be called in a loop. In each iteration, we will retrieve the
associated records from the specified record zone.
451
Chapter 19 Shared Books Tab
/*
sharedBookQuery function is called on a repeat till all
shared zones are queried for books shared by other users
*/
func sharedBookQuery(period: String, recordZone:
[CKRecordZone], counter: Int) {
let pred = NSPredicate(value: true)
let query = CKQuery(recordType: "Book", predicate:
pred)
let operation = CKQueryOperation(query: query)
CKContainer.default().privateCloudDatabase.add(operation)
if(recordZone.count != 0){
sharedData.perform(query, inZoneWith:
recordZone[counter].zoneID) { results, error in
DispatchQueue.main.async {
if let error = error {
print("Cloud Query Error - Fetch
Establishments: \(error)")
self.group.leave()
}else if let books = results {
self.syncQueue.async {
for task in books{
let record = Book()
452
Chapter 19 Shared Books Tab
record.bookname = task.
object(forKey: "bookname") as?
String
record.authorname = task.
object(forKey: "authorname") as?
String
record.recordID = task.recordID
record.genre = task.object(forKey:
"genre") as? String
record.status = task.
object(forKey: "status") as? String
record.reminder = task.
object(forKey: "reminder") as? Date
record.favorite = task.
object(forKey: "favorite") as? Int64
self.books.append(record)
453
Chapter 19 Shared Books Tab
}else{
self.group.leave()
}
}
Please note, we are using a counter to check if all record zones are exhausted;
when all zones are over, we leave the asynchronous loop by leaving the group.
There is no difference in this query from what we have done before, except the
following:
Summary
In this chapter, we learned how to accept a shared record and display the
records shared by other users on the Shared Tab of the Book Tracker App.
In the next chapter, we will learn how to Search a book from the iCloud
repository.
454
CHAPTER 20
Search Screen
This is the last Display screen we are going to code for our application.
Search will allow users to search for books using either the book name
or the author’s name. All books are stored in a Book Class variable called
Books. Based on the search input text provided by the user, we will search
for books and author names for any partial match. All books that will
match the search criteria will be stored in a temporary variable. The result
is then displayed using a table. When the user taps on a searched book
from the list, it will show the Book Detail Screen.
456
Chapter 20 Search Screen
Class Variables
We will have three class level variables. A variable to access the
common functions class for drawing the screen, a variable to access
DatabaseFunctions class to access the function to query the database and
a variable that will hold the books’ user search results. Table 20-2 has the
relevant code details.
/*
The below two variables are used to access the common
class functions and variables
*/
let commonFunctions = CommonFunctions()
let databaseFunctions = DatabaseFunctions()
/*
The search books variable defined below will hold the
books that meets the user search criteria
*/
var searchbooks = [DatabaseFunctions.Book]()
457
Chapter 20 Search Screen
Class UI Objects
We will have the following UI Objects for the search screen:
/*
A Label to define the Search Screen
a Search Textfied where user can input his/her search
criteria
a table view to diaplay the search result
*/
let applicationLabel:UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints =
false
label.backgroundColor = .white
label.numberOfLines = 4
label.textColor = .black
label.textAlignment = .center
return label
}()
(continued)
458
Chapter 20 Search Screen
let searchTextField:UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints =
false
textField.backgroundColor = .white
textField.borderStyle = .roundedRect
textField.attributedPlaceholder =
NSAttributedString(string: “Search Terms”, attributes:
[NSAttributedString.Key.foregroundColor: UIColor.gray])
textField.textColor = .black
return textField
}()
let searchTableView:UITableView = {
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints =
false
tableView.backgroundColor = UIColor.init(red: 255/255,
green: 253/255, blue: 208/255, alpha: 1)
tableView.layer.cornerRadius = 5
tableView.layer.borderWidth = 1
tableView.layer.borderColor = UIColor.black.cgColor
tableView.isScrollEnabled = false
return tableView
}()
459
Chapter 20 Search Screen
Screen Setup
This section will setup the search screen and will draw all UI Objects
460
Chapter 20 Search Screen
/*
The bookQuery function queries the database repository
for all books
*/
fileprivate func bookQuery(){
databaseFunctions.group.enter()
databaseFunctions.bookQuery()
databaseFunctions.group.notify(queue: .main) {
self.setup()
self.searchScreen()
}
}
461
Chapter 20 Search Screen
/*
bookQuery queries all books from the iCloud repository
*/
func bookQuery() {
let pred = NSPredicate(value: true)
let query = CKQuery(recordType: "Book", predicate:
pred)
let operation = CKQueryOperation(query: query)
CKContainer.default().privateCloudDatabase.
add(operation)
let ckRecordZoneID = CKRecordZone(zoneName:
"SharedZone")
operation.zoneID = ckRecordZoneID.zoneID
operation.recordFetchedBlock = { record in
let book = Book()
book.bookname = record["bookname"]
book.authorname = record["authorname"]
book.genre = record["genre"]
book.status = record["status"]
book.reminder = record["reminder"]
book.favorite = record["favorite"]
book.recordID = record.recordID
if(book.favorite == 1){
self.favbooks.append(book)
}
self.books.append(book)
}
(continued)
462
Chapter 20 Search Screen
Setup
Open your SearchViewController and add the function as shown in
Table 20-7. The setup function is called once the query is complete and
control is returned to the waiting asynchronous function.
463
Chapter 20 Search Screen
464
Chapter 20 Search Screen
1. func setup(){
Definition of the custom setup function
2. searchTableView.delegate = self
3. searchTableView.dataSource = self
4. searchTableView.register(BookTableViewCell.self,
forCellReuseIdentifier: "searchcell")
5. searchTableView.backgroundColor = UIColor.init(red:
202/255, green: 202/255, blue: 202/255, alpha: 1)
Setting up the table view delegate and data source reference. Notice that the Cell
Type is custom (this was defined and explained before). We are also setting the
table background color.
6. searchTextField.delegate = self
7. searchTextField.addTarget(self, action: #selector(textFiel
dEditingChanged), for: .editingChanged)
8. searchTextField.addTarget(self, action: #selector(textFiel
dEditingDidBegin), for: .editingDidBegin)
9. searchTextField.addTarget(self, action: #selector(textFiel
dEditingDidEnd), for: .editingDidEnd)
Setting the text field delegate. Also, defining three system-defined events for the
text field. The first is the when the value change on the text field (as the user
types we will do dynamic search, this event will be used to search as user types
and will display the result), the second function is invoked when editing begins
(when editing begins, we need to initialize the Search variables so that we are
ready for executing the search. We need to make sure Books class variable has
all books and search book class variable is empty), and the third function is when
the Editing ends (we need to reset the Book class variables so that we are ready
for the next search).
10. }
465
Chapter 20 Search Screen
applicationLabel.attributedText = NSAttributedString
(string: "Search Books", attributes: attributesMainHeader)
view.addSubview(searchTextField)
commonFunctions.setFieldLayout(mainField: searchTextField,
constraintField: applicationLabel, topAnchor: 60,
leftAnchor: 5, rightAnchor: -5, heightAnchor: 40)
}
466
Chapter 20 Search Screen
Editing Begin
When the Editing Begins event is triggered, we will assign all books
(stored in DatabaseFunctions books variable) to the class level variable
Searchbooks. Code details are defined in Table 20-9.
Editing Changed
Every time a user types in a letter, this function is called. As we will do
dynamic search, we will search for the books, draw the table, and refresh
the table, every time the user types in a letter. The code details are in
Table 20-10.
467
Chapter 20 Search Screen
databaseFunctions.books = searchbooks
databaseFunctions.books = searchbooks.filter {
item in return item.bookname.lowercased().
contains(searchTextField.text!.lowercased())
}
databaseFunctions.books.append(contentsOf:
searchbooks.filter {
item in return item.authorname.lowercased().
contains(searchTextField.text!.lowercased())
})
removeTableConstraints()
drawSearchTable()
(continued)
468
Chapter 20 Search Screen
}
1. @objc func textFieldEditingChanged(sender: UITextField) {
2. databaseFunctions.books = searchbooks
As you recall, when editing begins, we assign all database functions books to
the search book variable (this is because, for displaying the books, the table
view refers to “databaseFunctions.books” variable. “searchbooks” variable is
used as a temp variable to hold all books, and the “databaseFunctions.books”
variable will have a subset of books based on user search).
3. databaseFunctions.books = searchbooks.filter {
4. item in return item.bookname.lowercased().
contains(searchTextField.text!.lowercased())
5. }
We are now searching to see if any book name contains the user inputted
string – notice we are using contains with case insensitive search, so all books
will be filtered where the user-typed string exists anywhere in the book name.
6. databaseFunctions.books.append(contentsOf: searchbooks.
filter {
7. item in return item.authorname.lowercased().
contains(searchTextField.text!.lowercased())
8. })
(continued)
469
Chapter 20 Search Screen
Next, we are searching if any author name contains the user inputted string –
notice we are using contains with case insensitive search, so, all books will be
filtered where the user-typed string exists anywhere in the book name. notice we
are appending the value to “databaseFunctions.books” variable. So, now the
“databaseFunctions.books” variable has all books that meet the search criteria.
9. removeTableConstraints()
Remove the table drawn before. This is a custom function. The definition of the
same is explained in the latter part of this section.
10. drawSearchTable()
Draw the table. This is a custom function. The definition of the same is explained
in the latter part of this section.
11. if(databaseFunctions.books.count > 0){
12. searchTableView.isHidden = false
13. searchTableView.reloadData()
14. }else{
15. searchTableView.isHidden = true
16. }
If the search didn’t result in any books, we will keep the table hidden, otherwise
we will display the table and reload all values.
17. }
Editing End
When editing ends, we will store the “searchBooks” variable (which has
all books) to “databaseFunctions.books” variable. This way, we rest
the variables and are ready for another search. The code details are in
Table 20-11.
470
Chapter 20 Search Screen
471
Chapter 20 Search Screen
func drawSearchTable(){
view.addSubview(searchTableView)
commonFunctions.setFieldLayout(mainField:
searchTableView, constraintField: searchTextField,
topAnchor: 60, leftAnchor: 5, rightAnchor: -5,
heightAnchor: tableHeight)
}
472
Chapter 20 Search Screen
/*
Removing the Table Constraints
*/
func removeTableConstraints(){
searchTableView.removeConstraints(searchTableView.
constraints)
searchTableView.removeFromSuperview()
}
Table Function
Table function as shown in Table 20-15 has the following default functions:
473
Chapter 20 Search Screen
/*
The numberOfRowsInSection function will let the table
know how many books to display
*/
func tableView(_ tableView: UITableView,
numberOfRowsInSection section: Int) -> Int {
return databaseFunctions.books.count
}
/*
The cellForRowAt draws the cell and display the book and
author name
*/
func tableView(_ tableView: UITableView, cellForRowAt
indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifi
er: "searchcell", for: indexPath) as! BookTableViewCell
cell.bookLabelButton.contentHorizontalAlignment = .left
cell.bookLabelButton.setTitle(self.databaseFunctions.
books[indexPath.row].bookname + " by, " + self.
databaseFunctions.books[indexPath.row].authorname,
for: .normal)
cell.bookLabelButton.addTarget(self, action:
#selector(taskButtonAction), for: .touchUpInside)
cell.bookLabelButton.tag = indexPath.row
return cell
}
(continued)
474
Chapter 20 Search Screen
/*
On the custom cell when user taps on any cell displaying
the book and the author name the taskButtonAction
function is invoked, which in turn open the book details
view controller
*/
475
Chapter 20 Search Screen
Summary
This concludes our learning on creating a professional-looking Book
Tracking App. If you have followed through all the concepts in the book,
you must have on your iPhone a fully functional Book Tracker App with
data persistence in an iCloud repository. In the next section, we will learn
how to publish the application to the App Store.
476
CHAPTER 21
Packaging
and Releasing
Now that you have a fully functional professional looking App, it is
time now to learn how to package your App, get it tested in a controlled
audience, and finally, submit it to Apple App Store Review and approval.
Follow this chapter to learn the steps you need to follow to release your
App to the App Store.
1. 40w:40h Pixels
2. 60w:60h Pixels
3. 58w:58h Pixels
4. 87w:87h Pixels
5. 80w:80h Pixels
6. 120w:120h Pixels
7. 180w:180h Pixels
8. 1024w:1024h Pixels
You can use tools such as Photoshop to create the logos. I have used
GIMP, a freeware to create the Book Tracker App logo. Once you are done
with your design, you can then export the image to the relevant sizes to
PNG (or other file format) files.
Once the images are ready, please go to the navigator and click on
“Assets” and drag and drop the right image to the right image holder, as
shown in Figure 21-1.
478
Chapter 21 Packaging and Releasing
479
Chapter 21 Packaging and Releasing
Next, we need to create a shell for our App before we can publish the
App. Click on Add “New App”, as shown in the screen in Figure 21-3.
480
Chapter 21 Packaging and Releasing
https://developer.apple.com/support/app-store-connect/
481
Chapter 21 Packaging and Releasing
Menu Option
Once we have the shell ready, get back to the Xcode and from the Top
Menu, select Product ➤ Archive, as shown in Figure 21-5.
Distribute App
If there are no errors in the code, the archive will complete successfully,
and you will be prompted with a screen as shown in Figure 21-6. Click the
“Distribute App” option.
482
Chapter 21 Packaging and Releasing
Keep the default option selected “App Store Connect” page as shown
in Figure 21-7.
483
Chapter 21 Packaging and Releasing
484
Chapter 21 Packaging and Releasing
In the next screen(as shown in Figure 21-10) select all the options.
485
Chapter 21 Packaging and Releasing
2. Promotional Text
486
Chapter 21 Packaging and Releasing
8. Copyright information
All this information will be used by the App Review board to review
your App. Once done, you can submit for review.
487
Chapter 21 Packaging and Releasing
• CloudKit Database
From the main screen, click the CloudKit Database block, as shown in
Figure 21-15.
488
Chapter 21 Packaging and Releasing
• Select Database
Select the database from the top drop down. And select the option
Deploy Schema Changes from the left tree menu options, as shown in
Figure 21-16.
• Deploy Changes
489
Chapter 21 Packaging and Releasing
490
Chapter 21 Packaging and Releasing
portal and the users need to download Testflight on their iPhone. When
you publish an App for testing it appears in the Test Flight App. Users can
then download the App from Test Flight and use it like a beta version.
While testing, users can directly send feedback to the Test Flight
Center. Also, in case of crashes, a screen capture can also be sent. We can
then analyze the crashes in our Xcode build, fix issues, and release new
patches for users to test. Follow the following step to setup test users:
491
Chapter 21 Packaging and Releasing
Test-Driven Development
Modern software development demands Test Driven Development (TDD).
TDD has many benefits as outlined in the following:
492
Chapter 21 Packaging and Releasing
In the project, we can then create a class object of type UI Test Case
Class (as shown in Figure 21-20), and write our test method and code in
the class accordingly.
Summary
This was the final chapter of our book. We have learned not only how to
interact with an iCloud repository for performing CRUD functions, but
also creating an aesthetically appealing professional looking App. We also
learned how to extensively manipulate display views using Table Views
493
Chapter 21 Packaging and Releasing
by customizing the Table Cells, use events best coding for better user
interaction, and code-based screen designs.
Extension is another important leverage we have in iOS coding to
embed functionalities beyond the core development environment. We
learned about notification and iCloud Extensions in this book. Finally, in
this chapter, we learned how to make our code ready to be deployed in
production and send for the App Review process.
We hope you had fun writing code and seeing your App come to
life as you went through the 21 chapters of the book. Our intent was to
provide a comprehensive learning in native iOS development and a strong
bedrock for you to explore the art of possibilities in iOS programming.
Please let us know the Apps you will create using the learnings of the book.
Happy coding.
494
Index
A save book function
alert control, 293
Add book
asynchronous loop, 293
Add view controller, 92, 93
author’s name, 290
designing, 88
canSave variable, 290
running code, 93, 94
custom function, 293
running program, 114
genre name, 290, 291
UI objects, 97, 99, 100, 102
iCloud function query,
UI text field, 102, 104, 106, 107,
292, 293
109, 110, 112
Ok action, 294
view controller, 89, 91
reset field, 294
Add Book Screen, 258, 259
statements, 293
Add Book Tab Bar, 257, 258
AddBookViewController status, 291
class objects, 294, 295 validation, 287–289
class variables, 296 var variable, 289
DataBaseFunctions Class, 294 save book record
genre and status objects attribute, 299
array objects, 270 custom function, 298
class and static variable, 269 iCloud, 296–298
CommonVariables class, 268 SharedZone, 298
genre class, 270 zone ID information, 299
genre setup screen load event
function, 270–275 add book screen, 276–278
object type, 270 functions, 266
status class, 270 setup function, 267, 268
status setup function, 275 table functions
reset function, 300 display cell value, 281
496
INDEX
497
INDEX
498
INDEX
499
INDEX
I, J P, Q, R
iCloud database Packaging and releasing
create function file, 123 App distribution, 482, 483
functions, 130 App store connect, 483
post save, 131, 133 options, 485
reset field, 134–136 product details, 484
save book record, 124, 126, 128 sign in option, 485
saving book, 123, 124 upload option, 484, 486
validation, 115, 118, 122 application icons, 477
values, 129 application logo, 478
application setup, 486, 487
App store connect
K home screen, 479
Key-value storage, 17 new App popup screen, 481
new App screen, 480
archive and publish
L menu option, 482
Life cycle methods, 111 test flight account, 479
development database to
production database
M CloudKit console, 488
modifyRecordsCompletion deployment screen, 489, 490
Block, 173 project properties, 488
modifyRecordsOperation, 173 select database, 489
steps, 487
test users, 490, 491
N Xcode assets screen, 478
Non-fiction class, 40
Notification Center, 200
S
Search book, 456
O class variables, 457
Object-oriented programming, 143 constraints, removing, 472
500
INDEX
501
INDEX
502