Black Friday Black Friday
Black Friday ultimate sale! Get up to 80 % extra free points! More information here

Lesson 2 - First object-oriented app in Swift

Swift OOP First object-oriented app in Swift

In the previous lesson, Introduction to object-oriented programming in Swift, we introduced the object-oriented programming and went over what makes it more efficient than any other programming methodology. We already know that objects have fields and methods. We also know that to create an object we'd have to create a class first. Remember that a class is a pattern according to which we create instances later.

As we did in the Swift basic constructs course we'll create what must always be done when you encounter a new paradigm in programming, a "Hello World" program. A Hello object world program, to be precise!

We'll start by creating a new console Swift application (Command Line Tool) in Xcode as we're used to. In Project navigator on the left, right-click on our project folder and choose "New File...".

Creating a new file in Swift, Xcode

In the dialog, choose "Swift File" and continue by clicking on "Next". We'll name the new file Greeter.swift and confirm. We name classes using CamelCase, meaning that the first letter in every word in the class name is capitalized and we don't write spaces. The name, of course, shouldn't contain any accent characters, if your native language uses any, you can use them in strings, but never in identifiers

New file in Swift in Xcode

We can see a new source file now. In the comment block you can see, for example, the date of creation. We can also see import Foundation for importing the Swift basic functionality. We have to declare the class by ourselves, but it's easy:

class Greeter {

}

We're going to create a greeter object using this class later, which will be able to greet the user. You might've noticed by now that we're treating the program in a different way, for every action, there is some object responsible. One should not simply start by writing stuff into the main.swift file. In our case, it may seem useless, but in more complex applications it'll prove to be more than worthwhile :)

In Swift, classes aren't gathered in packages or namespaces, as in some other programming languages. Instead, the elementary unit is a Swift file, and it's not necessary to have all the code written within a class. For example, it's a common practice to create a Constants.swift file containing just String constants declared using let to have them all in one place to avoid typo errors.

The second "unit" in Swift are modules. Apple defines them as "single unit of distribution". A module is simply something that contains the intended functionality and can be used in various programs. Our new project is actually a module itself, but it doesn't make sense to use it in other applications. Similarly, Foundation is a module, or later UIKit, which is imported as a base of iOS applications instead of Foundation (because it contains Foundation).

Next, we'll add a greet() method to the Greeter class, which will be publicly visible and won't return a value or take any parameters.

In Swift, we declare methods as follows:

[access modifier] func [methodName]([parameters]) -> [return type]

We can omit the access modifier before the method, Swift uses internal as default, meaning the method can be accessed only from within the module (which is the whole application in our case). As next, we write the method's name. We name methods in a similar fashion as variables, using camelCase, but the very first letter is lowercase. Parentheses with parameters are required, we'll leave them empty since the method won't have any parameters. In the method body, we'll write code that prints a message to the console.

Our class will now look like this:

class Greeter {

    func greet() {
        print("Hello object world!")
    }

}

We're finished here for now, let's move to main.swift.

Now, in the main file, we'll create an instance of the Greeter class. It'll be the greeter object which we'll work with further. We store objects in variables and use the class name as the data type. An instance typically has the same name as its class, only the very first letter is lowercase. Let's declare the variable and then create a new instance of the Greeter class:

let greeter : Greeter
greeter = Greeter()

The first line says: "I want a greeter variable which will later contain a Greeter class instance inside. We've worked with variables like this before. On the first line, we only make the declaration, so let can be used. After the first assignment, it will no longer be possible to assign a new instance to the greeter variable.

On the second line, we create a new instance of the Greeter class using parentheses. We assign this instance to our variable.

When a new instance is created, the constructor is called. The constructor is a special class method, that's why we write the empty parentheses when creating an instance, we're calling this "creation" method. The constructor usually contains some initialization of the object's internal state, e.g. it initializes the fields with default values. We haven't declared a constructor in our class, that's why Swift created the implicit parameterless constructor. So creating an instance of an object is similar to calling a method. Of course, the entire code can be shortened to:

let greeter = Greeter()

Since now we have a Greeter class instance in a variable, we can let it greet the user. We'll call the greet() method as greeter.greet(). The main.swift file will now look like this:

import Foundation

let greeter : Greeter
greeter = Greeter()

greeter.greet()

Let's run the program.

Hello object world!

We've successfully made our first object-oriented app!

Now let's add a name parameter to our greet() method, so it could greet the user properly:

func greet(name: String) {
        print("Hi \(name)!")
}

We can see the syntax of the method parameter is the same as the syntax of a variable. If we wanted more parameters, we'd separate them with commas. Let's modify our main.swift file now:

let greeter : Greeter
greeter = Greeter()

greeter.greet(name: "Carl")
greeter.greet(name: "Peter")

Our code is now in a method and we're able to call it multiple times with different parameters. We don't have to copy "Hi ..." twice. We'll separate our code logically into methods from now. A big difference between Swift and other languages is providing the parameter name when calling a method. In our case name. The program won't work without it. If we wanted to remove the parameter names, we'd write _ before the parameter name in the method declaration. That way we won't have to write the names when calling the method.

func greet(_ name: String) {
        print("Hi \(name)!")
}

Calling the method would then look like this:

greeter.greet("Carl")

The output:

Hi Carl
Hi Peter

Let's add some field (attribute) to the class, e.g. a text where the greeting will be stored. We declare fields as variables as well. As it was with methods, if we omit the field's modifier, Swift assumes that it's iternal, which we're OK with. Let's modify our class:

class Greeter {

        var text: String = ""

        func greet(_ name: String) {
                print("\(text) \(name)!")
        }
}

We set the text field to an empty String. Otherwise, we'd have to deal with an Optional or a custom constructor. We'll now initialize the text of the instance created in main.swift:

let greeter = Greeter()
greeter.text = "Hi"
greeter.greet("Carl")
greeter.greet("Peter")
greeter.text = "Hello programmer"
greeter.greet("Richard")

The output:

Hi Carl
Hi Peter
Hello programmer Richard

In object-oriented design, it's not recommended to let each object control the input and output, i.e. printing lots of stuff into the console. Each object should have a single responsibility and shouldn't exceed its purpose. Let's make our object solely responsible for creating the greeting text, and we'll move the printing outside the object, i.e. to the main.swift file. The advantage to designing objects with a single responsibility is that they're then universal and reusable. The object can only output text to the console now, but we'll change it so the method will only return the text and it'll be up to the recipient to know what to do with it. We could also store greetings into files, print them on websites or process them further.

Since we want the method to return a String value, we'll set the String return type to it. We use the return keyword to return a value. Return terminates a method and returns a value. Any code in the method's body after the return will not be executed! Let's modify both classes:

The greet() method in Greeter.swift:

func greet(_ name: String) -> String {
    return "\(text) \(name)!"
}

The modified code in main.swift:

let greeter = Greeter()
greeter.text = "Hi"
print(greeter.greet("Carl"))
print(greeter.greet("Peter"))
greeter.text = "Hello programmer"
print(greeter.greet("Richard"))

Now, our code follows the guidelines of good OOP and over all programming practices.

Great! Our program already has some quality to it, despite it being relatively useless. If you want, you can try to create an object-oriented remake of our console calculator. In the next lesson, RollingDie in Swift - Constructors and Random numbers, we'll program a simple game. We'll make two objects, warriors, compete in an arena, which will also be an object). See? Now you have something to look forward to! ;-)


 

 

Article has been written for you by Filip Němeček
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
Activities (5)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!