Object-oriented programming (OOP) is a computer programming model which is the three-lettered magical concept on which almost all modern programming language stands. This tutorial is an introduction of Object-Oriented Programming with Swift.
What is an Instance?
Objects (also referred to as class instances) are self-contained modules of functionality that can be easily used and re-used as the building blocks for a software application.
Instances consist of data variables (called properties) and functions (called methods) that can be accessed and called on the instance to perform tasks and are collectively referred to as class members.
What is a Class?
Much as a blueprint or architect’s drawing defines what an item or a building will look like once it has been constructed, a class defines what an instance will look like when it is created. It defines, for example, what the methods will do and what the properties will be.
How to declaring a Swift Class?
Before an instance can be created, we first need to define the class ‘blueprint’ for the instance. In this chapter we will create a bank account class to demonstrate the basic concepts of Swift object-oriented programming.
In declaring a new Swift class we specify an optional parent class from which the new class is derived and also define the properties and methods that the class will contain. The basic syntax for a new class is as follows.
class NewClassName: ParentClass {
// Properties
// Instance Methods
// Type methods
}
The Properties section of the declaration defines the variables and constants that are to be contained within the class. These are declared in the same way that any other variable or constant would be declared in Swift.
The Instance methods and Type methods sections define the methods that are available to be called on the class and instances of the class.
To create an example outline for our BankAccount class, we would use the following.
class BankAccount {
}
Now that we have the outline syntax for our class, the next step is to add some instance properties to it.
When naming classes, note that the convention is for the first character of each word to be declared in uppercase (a concept referred to as UpperCamelCase). This contrasts with property and function names where lower case is used for the first character (referred to as lowerCamelCase).
How to declaring instance properties to a class?
Object-oriented programming is a concept referred to as data encapsulation. The idea behind data encapsulation is that data should be stored within classes and accessed only through methods defined in that class. Data encapsulated in a class are referred to as properties or instance variables.
Instances of our BankAccount class will be required to store some data, specifically a bank account number and the balance currently held within the account. Properties are declared in the same way any other variables and constants are declared in Swift. We can, therefore, add these variables as follows.
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
}
Having defined our properties, we can now move on to defining the methods of the class that will allow us to work with our properties while staying true to the data encapsulation model.
How to declaring and initializing a class instance ?
So far all we have done is define the blueprint for our class. In order to do anything with this class, we need to create instances of it. The first step in this process is to declare a variable to store a reference to the instance when it is created. We do this as follows.
var account1: BankAccount = BankAccount()
When executed, an instance of our BankAccount class will have been created and will be accessible via the account1 variable.
How to initializing and de-initializing a class instance ?
A class will often need to perform some initialization tasks at the point of creation. These tasks can be implemented by placing an init method within the class. In the case of the BankAccount class, it would be useful to be able to initialize the account number and balance properties with values when a new class instance is created. To achieve this, the init method could be written in the class as follows.
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
init(number: Int, balance: Float)
{
accountNumber = number
accountBalance = balance
}
func displayBalance()
{
print("Number \(accountNumber)")
print("Current balance is \(accountBalance)")
}
}
When creating an instance of the class, it will now be necessary to provide initialization values for the account number and balance properties as follows.
var account1 = BankAccount(number: 12312312, balance: 400.54)
Conversely, any cleanup tasks that need to be performed before a class instance is destroyed by the Swift runtime system can be performed by implementing the de-initializer within the class definition:
class BankAccount {
var accountBalance: Float = 0
var accountNumber: Int = 0
init(number: Int, balance: Float)
{
accountNumber = number
accountBalance = balance
}
deinit {
// Perform any necessary clean up here
}
func displayBalance()
{
print("Number \(accountNumber)")
print("Current balance is \(accountBalance)")
}
}
what is stored and computed Properties in swift?
Class properties in Swift fall into two categories referred to as stored properties and computed properties. Stored properties are those values that are contained within a constant or variable. Both the account name and number properties in the BankAccount example are stored properties.
A computed property, on the other hand, is a value that is derived based on some form of calculation or logic at the point at which the property is set or retrieved. Computed properties are implemented by creating getter and optional corresponding setter methods containing the code to perform the computation. Consider, for example, that the BankAccount class might need an additional property to contain the current balance less any recent banking fees. Rather than use a stored property, it makes more sense to use a computed property which calculates this value on request. The modified BankAccount class might now read as follows:
class BankAccount {
var accountBalance: Float = 0 var accountNumber: Int = 0; let fees: Float = 25.00
var balanceLessFees: Float {
get {return accountBalance - fees
} }
init(number: Int, balance: Float)
{
accountNumber = number
accountBalance = balance
}
}
The above code adds a getter that returns a computed property based on the current balance minus a fee amount. An optional setter could also be declared in much the same way to set the balance value less fees.
var balanceLessFees: Float {
get {return accountBalance - fees }
set(newBalance)
{
accountBalance = newBalance - fees
}
}
The new setter takes as a parameter a floating-point value from which it deducts the fee value before assigning the result to the current balance property. Although these are computed properties, they are accessed in the same way as stored properties using dot-notation. The following code gets the current balance less the fees value before setting the property to a new value.
var balance1 = account1.balanceLessFees
account1.balanceLessFees = 12123.12
What is Lazy Stored Properties in ios?
There are several different ways in which a property can be initialized, the most basic being direct assignment as follows:
var myProperty = 10
Alternatively, a property may be assigned a value within the initializer:
class MyClass {
let title: String
init(title: String) {
} } self.title = title
For more complex requirements, a property may be initialized using a closure:
class MyClass {
var myProperty: String = {
var result = resourceIntensiveTask()
result = processData(data: result)
}() return result
}
Particularly in the case of a complex closure, there is the potential for the initialization to be resource intensive and time consuming. When declared in this way, the initialization will be performed every time an instance of the class is created, regardless of when (or even if) the property is actually used within the code of the app. Also, situations may arise where the value assigned to the property may not be known until a later stage in the execution process, for example after data has been retrieved from a database or user input has been obtained from the user. A far more efficient solution in such situations would be for the initialization to take place only when the property is first accessed. Fortunately, this can be achieved by declaring the property as lazy as follows:
class MyClass {
lazy var myProperty: String = {
var result = resourceIntensiveTask() result = processData(data: result)
}() return result }
When a property is declared as being lazy, it is only initialized when it is first accessed, allowing any resource intensive activities to be deferred until the property is needed and any initialization on which the property is dependent to be completed.
Note that lazy properties must be declared as variables (var).
How to Using self in Swift?
The Swift programming language also provides the self property type for this purpose and it is, therefore, perfectly valid to write code which reads as follows.
class MyClass {
var myNumber = 1
func addTen() {
self.myNumber += 10
}
}
In this context, the self prefix indicates to the compiler that the code is referring to a property named myNumber which belongs to the MyClass class instance. When programming in Swift, however, it is no longer necessary to use self in most situations since this is now assumed to be the default for references to properties and methods.
In the following code, for example, the first print statement will output the value passed through to the function via the myNumber parameter while the second print statement outputs the number assigned to the myNumber class property (in this case 10):
class MyClass {
var myNumber = 10 // class property
func addTen(myNumber: Int) {
print(myNumber) // Output the function parameter value
print(self.myNumber) // Output the class property value
}
}
Whether or not to use self in most other situations is largely a matter of programmer preference. Those who prefer to use self when referencing properties and methods can continue to do so in Swift. Code that is written without use of the self property type (where doing so is not mandatory) is, however, just as valid when programming in Swift.
What is Swift Protocols?
By default, there are no specific rules to which a Swift class must conform as long as the class is syntactically correct. In some situations, however, a class will need to meet certain criteria in order to work with other classes. This is particularly common when writing classes that need to work with the various frameworks that comprise the iOS SDK. A set of rules that define the minimum requirements which a class must meet is referred to as a Protocol. A protocol is declared using the protocol keyword and simply defines the methods and properties that a class must contain in order to be in conformance. When a class adopts a protocol, but does not meet all of the protocol requirements, errors will be reported stating that the class fails to conform to the protocol.
Consider the following protocol declaration. Any classes that adopt this protocol must include both a readable String value called name and a method named buildMessage() which accepts no parameters and returns a String value.
protocol MessageBuilder {
var name: String { get }
func buildMessage() -> String
}
Below, a class has been declared which adopts the MessageBuilder protocol:
class MyClass: MessageBuilder { }
Unfortunately, as currently implemented, MyClass will generate a compilation error because it contains neither the name variable nor the buildMessage() method as required by the protocol it has adopted. To conform to the protocol, the class would need to meet both requirements, for example:
class MyClass: MessageBuilder {
var name: String
init(name: String) {
} self.name = name
func buildMessage() -> String {
"Hello " + name
}
}
How to use Opaque Return Types in swift?
Now that protocols have been explained it is a good time to introduce the concept of opaque return types. As we have seen in previous chapters, if a function returns a result, the type of that result must be included in the function declaration. The following function, for example, is configured to return an Int result.
func doubleFunc1 (value: Int) -> Int {
return value * 2
}
Instead of specifying a specific return type (also referred to as a concrete type), opaque return types allow a function to return any type as long as it conforms to a specified protocol. Opaque return types are declared by preceding the protocol name with the some keyword. The following changes to the doubleFunc1() function, for example, declare that a result will be returned of any type that conforms to the Equitable protocol.
func doubleFunc1(value: Int) -> some Equatable {
value * 2
}
To conform to the Equatable protocol, which is a standard protocol provided with Swift, a type must allow the underlying values to be compared for equality. Opaque return types can, however, be used for any protocol, including those you create yourself.
Given that both the Int and String concrete types are in conformance with the Equatable protocol, it is possible to also create a function that returns a String result.
func doubleFunc2(value: String) -> some Equatable {
value + value
}
Although these two methods return entirely different concrete types, the only thing known about these types is that they conform to the Equatable protocol. We therefore know the capabilities of the type, but not the actual type.
In fact, we only know the concrete type returned in these examples because we have access to the source code of the functions. If these functions resided in a library or API framework for which the source is not available to us, we would not know the exact type being returned. This is intentional and designed to hide the underlying return type used within public APIs. By masking the concrete return type, programmers will not come to rely on a function returning a specific concrete type or risk accessing internal objects which were not intended to be accessed. This also has the benefit that the developer of the API can make changes to the underlying implementation (including returning a different protocol compliant type) without having to worry about breaking dependencies in any code that uses the API.
This raises the question of what happens when an incorrect assumption is made when working with the opaque return type. Consider, for example, that the assumption could be made that the results from the doubleFunc1() and doubleFunc2() functions can be compared for equality:
let intOne = doubleFunc1(value: 10)
let stringOne = doubleFunc2(value: "Hello")
if (intOne == stringOne) {
print("They match")
}
Working on the premise that we do not have access to the source code for these two functions there is no way to know whether the above code is valid. Fortunately, although we, as programmers, have no way of knowing the concrete type returned by the functions, the Swift compiler has access to this hidden information. The above code will, therefore, generate the following syntax error long before we get to the point of trying to execute invalid code:
widely when developing apps in SwiftUI (the some keyword will appear frequently in SwiftUI View declarations). SwiftUI advocates the creation of apps by composing together small, reusable building blocks and refactoring large view declarations into collections of small, lightweight subviews. Each of these building blocks will typically conform to the View protocol. By declaring these building blocks as returning opaque types that conform to the View protocol, these building blocks become remarkably flexible and interchangeable, resulting in code that is cleaner and easier to reuse and maintain.Binary operator ‘==’ cannot be applied to operands of type ‘some Equatable’ (result of ‘doubleFunc1(value:)’) and ‘some Equatable’
(result of ‘doubleFunc2(value:)’)
Opaque return types are a fundamental foundation of the implementation of the SwiftUI APIs and are used
- How to remove duplicate elements from an array with Swift 5
- How to convert an APNS token to String from Data
- What is difference between enumeration and structure?
- How to store Model Class data in User Defaults in Swift?
- Adding a Custom Font to Your IOS App
- How to big numbers YAxis format values convert to Indian Numbering Format like k(thousand), L(lakh), Cr(crore), Ar(Arab) and etc.on iOS barchart Swift
- Convert number to indian rupee format in Swift 5 iOS
- How to display native iOS activity indicator in xcode Swift 5
- How to Programmatically Create an Excel Spreadsheet View iOS Swift
-------------------------------------------
0 Comments