DEV Community

Kno Harutyunyan
Kno Harutyunyan

Posted on

iOS Interview Questions 2023

Hi folks, I want to share my knowledge about the Swift programming language and the UIKit framework. I encountered these questions while preparing for an iOS interview. We are here to dive deep into these questions and discuss them together.

This article delves into the following topics.

  1. Classes VS Structures
  2. Closures
  3. Optionals
  4. Generics
  5. Extensions, Protocols, and Delegation
  6. Access Control
  7. ARC — Automatic Reference Counting
  8. SOLID Principles
  9. MVC, MVVM, and MVP design patterns
  10. GCD — Grand Central Dispatch in iOS
  11. Application Life Cycle in iOS
  12. ViewController Life Cycle in iOS
  13. I Published my first app to the App Store(MoveItem & GuessPicture)🥶 … coming soon new topics 🚀

Let’s start our journey 🚀

Classes VS Structures

The classes and structures have a few differences. Let’s go through all of them.

  1. The classes are reference types.
  2. The Structures are value types.

  3. The classes have an inheritance which means one class can inherit from another class.

  4. The structures don’t have an inheritance and can’t inherit from another structure.

  5. The classes don’t give us a default initializer when our property doesn’t have an initial value and we should implement our initializer.

  6. The structures give us a default initializer even when our property doesn’t have an initial value.

"Class Examples"
class Base {
    var name: String // Class 'Base' has no initializers (Remember Rule 5)
}
// Solution in below
class Base {

    // give an initial value to handle error
    var name: String = "Base"

    // implement initializer for that value
    init(name: String) {
        self.name = name
    }
}

"Struct Examples"
struct Person {
    var age: Int
    var name: String
} //We don't receive this error "Struct 'Person' has no initializers" because Structures have a default initializer.
  // Remember Rule 6
let person = Person(age: 20, name: "John")
###############################################################################

"Class Examples"
class Parent {
    let name: String = "Some Value"
}

class Child: Parent {}

let child = Child()
print(child.name) // outcome is "Some Value"

"Struct Examples"
struct Parent {}

struct Child: Parent {} // Inheritance from non-protocol type 'Parent'
// If we remember the rule of number 4 we can know that structures don't have inheritance 
###############################################################################

"Class Examples"
class Person {
    var name: String = "Dave"
}

let p1 = Person()
let p2 = p1

print(p1.name) // outcome is Dave
print(p2.name) // outcome is Dave

p2.name = "John"

print(p1.name) // outcome is John
print(p2.name) // outcome is John
// If you remember I said that classes are reference-type and when we create an instance of that class and assign it to another instance they reference each other memory cells and know each other.

"Struct Examples"
struct Person {
    var name: String = "Dave"
}

var p1 = Person()
var p2 = p1

print(p1.name) // outcome is Dave
print(p2.name) // outcome is Dave

p2.name = "John"

print(p1.name) // outcome is Dave
print(p2.name) // outcome is John
// The structures are value types, which means that when we create an instance of a structure and assign it to another instance of the structure, it doesn't have a reference to the main instance's memory cells and cannot change the main instance's property values.
Enter fullscreen mode Exit fullscreen mode

Closures

We can say that closures have a lot of similarity with functions.
Closures have a body, a return type, and can also receive arguments.
The most important thing to know is that closures are reference types.
If you are familiar with other programming languages such as C/C++, you can compare them to Lambda functions.

Closure Expression Syntax

{ (<#parameters#>) -> <#return type#> in
   <#statements#>
}

Examples
var maxOfNumbers: (Int, Int) -> Int = { num1, num2 in
    return num1 > num2 ? num1 : num2
} // Closure

or

func maxOfNumbersAsFunction(num1: Int, num2: Int) -> Int {
    return num1 > num2 ? num1 : num2
} // Function

let num1 = 30
let num2 = 55
let maxValue = maxOfNumbers(num1, num2)

print(maxValue) // 55

let numbers = [1, 2, 3, 4, 5]

let sorted = numbers.sorted { num1, num2 in
    num1 > num2
}

let sorted2 = numbers.sorted { $0 > $1 }

let sorted3 = numbers.sorted(by: >)

print(sorted) // [5, 4, 3, 2, 1]
print(sorted2) // [5, 4, 3, 2, 1]
print(sorted3) // [5, 4, 3, 2, 1]
Enter fullscreen mode Exit fullscreen mode

I also want to talk about ‘escaping closure.’ This is a very useful feature when you want to give life to your objects outside of closure blocks. When I started reading the documentation, I didn’t initially understand why we needed this. But when we work with UIKit without ‘escaping closures,’ our lives become difficult because, when making API calls and wanting to pass data to controllers, we rely on ‘escaping closures’. Escaping closures are a valuable tool for managing asynchronous operations and passing data between different parts of your app.

struct User {
    let name: String
}

enum UserError: Error {
    case invalidURL
}

func getUserFromServer(completion: @escaping (Result<User, Error>) -> Void){
    let user = User(name: "") // here should be api call to server

    // success
    completion(.success(user))

    // error
    completion(.failure(UserError.invalidURL))
}
Enter fullscreen mode Exit fullscreen mode

Optionals

Let’s discuss optionals and how to safely unwrap them.

We can create variables that can be optional(nil) or non-optional.

var name: String? // can be nil
var age: Int // cannot be nil
Enter fullscreen mode Exit fullscreen mode

We have many ways to handle nil a value which have a variable.

// if 
if name != nil {
    print(name!)
} else {
    print("nil")
}

// if let
if let name = name {
    // name has a value
    print(name)
} else {
    print("nil")
}

// guard
guard let name = name else { return }
// when the name is nil else block work

// (??)
let nonNilName = name ?? ""
//We can use this operator "??" to check whether a value is nil or not. When a value is nil it takes value after this operator "??"

// Optional binding
if let userName = user?.name? {}
//When we have structure or classes and that classes can be nil and that class can contain a variable which also can be nil we use optional binding.

// unwrap

var x: int?
print(x!) // crash our app
// when we have a nil value variable we can implicitly unwrap that using this operator "!" but I don't recommend it because you can crash your app.

Enter fullscreen mode Exit fullscreen mode

Generics

The generics are the most powerful feature which gives us an opportunity to create flexible, effective, and reusable code. We can create one function that can receive the argument Any type.

func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

var num1 = 99
var num2 = 77

swap(&num1, &num2)

print(num1, num2) // 77 99

var num3 = 23.45
var num4 = 78.45

swap(&num3, &num4)

print(num3, num4) // 78.45 23.45

var s1 = "Dave"
var s2 = "John"

swap(&s1, &s2)

print(s1, s2) // John Dave
Enter fullscreen mode Exit fullscreen mode

As you see our function is generic and can get any type.

Extensions, Protocols, and Delegation

  1. Extensions Extensions are one of the most powerful features that Swift offers. The first time I encountered extensions, I immediately thought about the ‘Open/Close SOLID principle.’ Let’s first understand what the ‘Open/Close principle’ means: it suggests that classes, structures, enums, protocols, and so on should be open for extension but closed for modification. I believe Swift’s extensions provide us with that opportunity.
extension Int {
    func multiplyInt(of number: Int) -> Int {
        return self * number
    }
}

let num: Int = 5

let multiplyIntOf5 = num.multiplyInt(of: 5)

print(multiplyIntOf5) // 25
// As you see I added an extension for Int. You can add extensions where you want.

// We also can add extensions for our custom types.
class Person {
    var fname: String = "John"
    var lname: String = "Macuire"
}

extension Person {
    func fullName() {
        print(fname + " " + lname)
    }
}

let person = Person()

person.fullName()
Enter fullscreen mode Exit fullscreen mode
  1. Protocols Protocols are one of the most important topics in the Swift programming language. They provide us with numerous opportunities, such as preventing code reusability, enabling polymorphism, implementing delegate patterns, and more. The protocols can contain the blueprint of the function and the protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. When classes, structures, and enums adopt protocols they should conform (implement) all functions which protocols have.
protocol Animal {
    func sleep()
    func eat()
    func drinkWater()
}

class Dog: Animal {
    // Type 'Dog' does not conform to protocol 'Animal'
}

class Chicken: Animal {
    // Type 'Chicken' does not conform to protocol 'Animal'
}

class Eagle: Animal {
    // Type 'Eagle' does not conform to protocol 'Animal'
}

// I received those errors because I adopted protocols but did not conform. Let's do that together.

class Dog: Animal {
    func sleep() {
        print("sleep")
    }

    func eat() {
        print("eat")
    }

    func drinkWater() {
        print("drinkWater")
    }
}

class Chicken: Animal {
    func sleep() {
        print("sleep")
    }

    func eat() {
        print("eat")
    }

    func drinkWater() {
        print("drinkWater")
    }
}

class Eagle: Animal {
    func sleep() {
        print("sleep")
    }

    func eat() {
        print("eat")
    }

    func drinkWater() {
        print("drinkWater")
    }
}

// As you see I conform to protocol and implement all of their function. 
Enter fullscreen mode Exit fullscreen mode

The protocols can have computed property but don’t stored property.

protocol ProtocolForGetFullName {
    var fullname: String { get }
}

struct Person: ProtocolForGetFullName {
    var fullname: String
}

let person = Person(fullname: "Dave")
print(person.fullname)

Enter fullscreen mode Exit fullscreen mode
  1. Delegation Delegation in Swift is a design pattern that involves defining a protocol to establish a contract for communication between two objects. One object should conform to the protocol, while the second object delegates tasks to it.
protocol ReminderDelegate: AnyObject {
    func didWakeUpAtSix()
}

class Person: ReminderDelegate {
    let reminder = Reminder()

    var isWakeUp: Bool = false

    func didWakeUpAtSix() {
        self.isWakeUp = true
    }

    init() {
        reminder.delegate = self
        reminder.personShouldWakeUpAtSix()
    }
}

class Reminder {
    weak var delegate: ReminderDelegate?

    func personShouldWakeUpAtSix() {
        self.delegate?.didWakeUpAtSix()
    }
}

let person = Person()
print(person.isWakeUp) // true
Enter fullscreen mode Exit fullscreen mode

As you see we created the ReminderDelegate protocol which has only one function which is didWakeUpAtSix. I have created the Person class which conforms to the ReminderDelegate. Also, I have the Reminder class which delegates tasks to the Person class. If you don’t understand what is going on here you can copy past code and play with them and also if you have more questions you can write me.

Access Control

Access control in Swift is a language feature that allows you to restrict the visibility and accessibility of various parts of your code, such as classes, structures, enums, functions, variables, and so on. With access controls, you can determine who can have access to parts of code or methods.

  1. open
    Note: Accessible from anywhere.
    With the “open” keyword our data is accessible from anywhere what that means? When you define your class with the “open” keyword we can see that code from another module, packages, and so on. The “open” keyword is very useful when you want to create your own framework or libraries as Combine, Alamofire, and so on.

  2. public
    Note: Accessible from anywhere.
    You can wonder why the “public” keyword gives the same access controls but there is one difference from the “open” keyword. Both “public” and “open” allow access from other modules, “open” additionally allows other modules to subclass and override classes and their members but “public” does give that permission.

  3. internal (by default)
    Note: Declarations are accessible only within the defined module.
    In Swift, when you define classes, structures, methods, and variables, they have internal access by default. This means you can only access these methods within that module.

  4. fileprivate
    Note: Declarations are accessible only within the current swift file.
    In Swift, you can use the ‘fileprivate’ keyword to restrict the visibility of a declaration to the current Swift file.

  5. private
    Note: Accessible within the same declaration or extension.
    The most restrictive access level. Private entities can only be accessed from within the same enclosing declaration or extension. Useful for encapsulating implementation details within a class or structure.

  6. private(set)
    Note: It has the same accessibility as the “private” but I can see(get) value when defining the variable as the “private(set)”.

  7. final
    Note: Prevent inheritance
    When we define our classes with the “final” keyword we prevent inheritance what that means? For example, I have the “Parent” class which is defined as “final” and also I have the “Child” class which want to inherit from the “Parent” class but cannot because the “Parent” is “final”.

I published(submitted) my first app to the App Store(MoveItem & GuessPicture)🚀🔥

App Link

ARC — Automatic Reference Counting

Since ARC is a big topic, I’ve created another article specifically dedicated to it. You can find it here: link 🚀

SOLID principle

Since SOLID is a big topic, I’ve created another article specifically dedicated to it. You can find it here: link 🚀

MVC, MVVM, and MVP design patterns

Since MVC, MVVM, and MVP a big topics, I’ve created another article specifically dedicated to it. You can find it here: link 🚀

GCD — Grand Central Dispatch in iOS

Since GCD — Grand Central Dispatch is a big topic, I’ve created another article specifically dedicated to it. You can find it here: link 🚀

Application Life Cycle in iOS

Since the Application Life Cycle in iOS is a big topic, I’ve created another article specifically dedicated to it. You can find it here: link 🚀

ViewController Life Cycle in iOS

Since the ViewController Life Cycle in iOS is a big topic, I’ve created another article specifically dedicated to it. You can find it here: link 🚀

Thank you for your attention while reading this article and I will be very happy if you share your opinions about this article.

I hope to see you soon with your suggestions and problems please write to me on LinkedIn.

Top comments (0)