All interview topics
Full-Stack, Web & Mobile

Swift Interview Questions and Answers

Practice Swift interview questions and answers organized by difficulty, with revealable explanations and related topics.

72 questionsEntry → Expert

Junior questions

6 questions
  1. Question 1
    Junior

    What are Extensions used for in Swift?

    • Swift
    Reveal answer

    Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code. Extensions are similar to categories in Objective-C Extensions in Swift can: Add computed instance properties and computed type properties Define instance methods and type methods Provide new initializers Define subscripts Define and use new nested types Make an existing type conform to a protocol

  2. Question 2
    Junior

    What are Extensions used for in Swift?

    • Swift
    Reveal answer

    Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code. Extensions are similar to categories in Objective-C Extensions in Swift can: Add computed instance properties and computed type properties Define instance methods and type methods Provide new initializers Define subscripts Define and use new nested types Make an existing type conform to a protocol

  3. Question 3
    Junior

    What is a Serial Queue?

    • Swift
    Reveal answer

    A Serial Queue allows us to perform only one task at a time, no matter the way of execution, i.e. Synchronous or Asynchronous . In short, the code executed by Serial DispatchQueue is not parallel and has to wait for the first task to complete. This way of execution is also known as First In, First Out (FIFO). All the queues need to wait for the completion of the previous queue. By default, DispatchQueue is a serial queue. Consider:

    let queue = DispatchQueue(label: "com.swiftpal.dispatch.serial")
    queue.async() {
      Thread.sleep(forTimeInterval: 3)
      // Wait for 3 seconds
      print("Task 1 Done")
    }
    queue.async() {
      Thread.sleep(forTimeInterval: 1)
      // Wait for 1 second.
      print("Task 2 Done")
    }/* Output:
    Task 1 Done
    Task 2 Done
    */
    
  4. Question 4
    Junior

    What is the difference between Upcast and Downcast in Swift?

    • Swift
    Reveal answer

    The upcast, going from a derived class to a base class, can be checked at compile time and will never fail. However, downcasts can fail since you can’t always be sure about the specific class. If you have a UIView, it’s possible it’s a UITableView or maybe a UIButton. downcasts must be either optional with as? or “forced failable” with as! when sure about the type

  5. Question 5
    Junior

    What’s the difference between == and === ?

    • Swift
    Reveal answer

    == operator checks if the values are the same, comparing value types. "equal to" === operator checks if the references point the same instance (both point to the same memory address), comparing reference types. "identical to"

  6. Question 6
    Junior

    How to iterate a loop with index and element in Swift?

    • Swift
    Reveal answer

    Swift 5 provides a method called enumerated() for Array :

    let list = ["Car", "Bike", "Plane", "Boat"]
    for (index, element) in list.enumerated() {
      print(index, ":", element)
    }/*
    prints:
    0 : Car
    1 : Bike
    2 : Plane
    3 : Boat
    */
    

Mid questions

28 questions
  1. Question 1
    Mid

    What's the main difference between the Array , Set and Dictionary collection type?

    • Swift
    Reveal answer

    Arrays are effectively ordered lists and are used to store lists of information of the same type in cases where order is important. The same value can appear in an array multiple times at different positions. Sets are different in the sense that order does not matter and these will be used in cases where order does not matter. Dictionaries are used to store key, value pairs and are used when you want to easily find a value using a key, just like in a dictionary. Unlike items in an array, items in a dictionary do not have a specified order.

  2. Question 2
    Mid

    Rewrite this code in a Swift way

    • Swift
    Reveal answer

    Problem

    Consider:

    let list = [Int](1...5)
    var arrayOfTuples = [(Int, Int)]()
    for (index, element) in list.enumerated() {
      arrayOfTuples += [(index, element)]
    }
    print(arrayOfTuples)
    // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
    

    Can you rewrite this code in a more "swiftier" way?

    Solution

    Yeap, the way to do it is:

    let list = [Int](1...5)
    let arrayOfTuples = Array(list.enumerated())
    print(arrayOfTuples)
    // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element:
    3), (offset: 3, element: 4), (offset: 4, element: 5)]
    

    or with map :

    let list = [Int](1...5)
    let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
    print(arrayOfDictionaries)
    // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]
    
  3. Question 2
    Mid

    Explain the defer usage in Swift

    • Swift
    Reveal answer

    A defer statement is used for executing code just before transferring program control outside of the scope that the statement appears in.

    func updateImage() {
      defer { print("Did update image") }
      print("Will update image")
      imageView.image = updatedImage
    }// Will update Image
    // Did update image
    

    The most common use case seen around is opening and closing a context within a scope, for example when handling access to files. A FileHandle requires to be closed once the access has been finished. You can benefit from the defer statement to ensure you don’t forget to do this.

    func writeFile() {
      let file: FileHandle? = FileHandle(forReadingAtPath: filepath)
      defer { file?.closeFile() }// Write changes to the file
    }
    
  4. Question 3
    Mid

    How to sort array of custom objects by property value in Swift?

    • Swift
    Reveal answer

    For in-place sorting consider:

    var images : [imageFile] = []
    images.sorted(by: { $0.fileID > $1.fileID })
    // swift 3+
    images.sorted { $0.fileID < $1.fileID }
    // exploiting a trailing closure
    

    If you are going to do this repeatedly and prefer to define a function, one way is: func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool { return this.fileID > that.fileID

    }
    ...
    images.sort(by: sorterForFileIDASC)
    
  5. Question 3
    Mid

    Explain the difference between DispatchQueue.main.async and DispatchQueue.main.sync ?

    • Swift
    Reveal answer

    When you use async it lets the calling queue move on without waiting until the dispatched block is executed. On the contrary sync will make the calling queue stop and wait until the work you've dispatched in the block is done. Therefore sync is subject to lead to deadlocks. Try running DispatchQueue.main.sync from the main queue and the app will freeze because the calling queue will wait until the dispatched block is over but it won't be even able to start (because the queue is stopped and waiting). When to use sync ? When you need to wait for something done on a DIFFERENT queue and only then continue working on your current queue Example of using sync: On a serial queue you could use sync as a mutex in order to make sure that only one thread is able to perform the protected piece of code at the same time.

  6. Question 4
    Mid

    When should I use deinit ?

    • Swift
    Reveal answer

    It's not required that you implement that method, but you can use it if you need to do some action or cleanup before deallocating the object. Consider:

    struct Bank {
      static var coinsInBank = 10_000
      static func vendCoins(var numberOfCoinsToVend: Int) -> Int {
        numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank)
        coinsInBank -= numberOfCoinsToVend
    

    return numberOfCoinsToVend

    }
    static func receiveCoins(coins: Int) {
      coinsInBank += coins
    }
    }
    class Player {
      var coinsInPurse: Int
      init(coins: Int) {
        coinsInPurse = Bank.vendCoins(coins)
      }
    func winCoins(coins: Int) {
      coinsInPurse += Bank.vendCoins(coins)
    }
    deinit {
      Bank.receiveCoins(coinsInPurse)
    }
    }
    

    So whenever the player is removed from the game, its coins are returned to the bank.

  7. Question 4
    Mid

    How to append one array to another array in Swift?

    • Swift
    Reveal answer

    Problem

    Consider:

    var first = ["John", "Paul"]
    let second = ["George", "Ringo"]
    

    How to append one array to another array in Swift? Note: the first array mutable so we can join the second array to it.

    Solution

    One option for joining arrays is the append(contentsOf:) method, used like this:

    first.append(contentsOf: second)
    

    Another option is using the += operator, which is overloaded for arrays to combine elements:

    first += second
    

    The third option is to use a regular + to create a new array by combining two others:

    let third = first + second
    

    All three options produce the same resulting array.

  8. Question 5
    Mid

    What’s a strong reference, and why do we need it?

    • Swift
    Reveal answer

    Strong references increase the retain count of instances they reference (by 1). This prevents the Automatic Reference Counting (ARC) from removing the object from memory, as long as the object is in use. In essence,

    **as long as anything has a strong reference to an object, it will not be deallocated. **
    

    You can assert the following truths about strong references: In Swift, a strong reference is the default. When you create a variable, constant or property for a reference type, a strong reference is created by default. This is also true for passing references into functions or closures. An instance is only deallocated when its retain count is zero; when no other objects hold onto it. A strong reference increases the retain count by one, so the strong reference must be broken before the instance it references can be deallocated.

  9. Question 6
    Mid

    When to use strong , weak and unowned references?

    • Swift
    Reveal answer

    To determine if you even need to worry about strong, weak, or unowned, ask, “Am I dealing with reference types”. If you’re working with Structs or Enums, ARC isn’t managing the memory for those Types and you don’t even need to worry about specifying weak or unowned for those constants or variables. Strong references are fine in hierarchical relationships where the parent references the child, but not vice- versa. In fact, strong references are the most appropraite kind of reference most of the time. When two instances are optionally related to one another, make sure that one of those instances holds a weak reference to the other. When two instances are related in such a way that one of the instances can’t exist without the other, the instance with the mandatory dependency needs to hold an unowned reference to the other instance.

  10. Question 6
    Mid

    When to use a set rather than an array in Swift?

    • Swift
    Reveal answer

    You should use a set rather than an array if all the following criteria are true:

    1. You intend to add each item only once. Sets never allow duplicates.
    2. You don’t care about the order of the items in the set.
    3. You don’t need to use APIs that require arrays.
    4. You’re storing Hashable types, either your own or one of Swift’s built-in types likes strings and integers. Sets use hash values for fast look up of items.
  11. Question 7
    Mid

    What’s a strong reference, and why do we need it?

    • Swift
    Reveal answer

    Strong references increase the retain count of instances they reference (by 1). This prevents the Automatic Reference Counting (ARC) from removing the object from memory, as long as the object is in use. In essence,

    **as long as anything has a strong reference to an object, it will not be deallocated. **
    

    You can assert the following truths about strong references: In Swift, a strong reference is the default. When you create a variable, constant or property for a reference type, a strong reference is created by default. This is also true for passing references into functions or closures. An instance is only deallocated when its retain count is zero; when no other objects hold onto it. A strong reference increases the retain count by one, so the strong reference must be broken before the instance it references can be deallocated.

  12. Question 7
    Mid

    Explain the defer usage in Swift

    • Swift
    Reveal answer

    A defer statement is used for executing code just before transferring program control outside of the scope that the statement appears in.

    func updateImage() {
      defer { print("Did update image") }
    
      print("Will update image")
      imageView.image = updatedImage
    }// Will update Image
    // Did update image
    

    The most common use case seen around is opening and closing a context within a scope, for example when handling access to files. A FileHandle requires to be closed once the access has been finished. You can benefit from the defer statement to ensure you don’t forget to do this.

    func writeFile() {
      let file: FileHandle? = FileHandle(forReadingAtPath: filepath)
      defer { file?.closeFile() }// Write changes to the file
    }
    
  13. Question 8
    Mid

    When to use @objc attribute?

    • Swift
    Reveal answer

    By default Swift generates code that is only available to other Swift code, but if you need to interact with the Objective-C runtime – all of UIKit, for example – you need to tell Swift what to do. That’s where the @objc attribute comes in: when you apply it to a class or method it instructs Swift to make those things available to Objective-C as well as Swift code. So, any time you want to call a method from a UIBarButtonItem or a Timer , you’ll need to mark that method using @objc so it’s exposed – both of those, and many others, are Objective-C code. Consider:

    class MyController: UIViewController {
      @objc func authenticateUser() {
      }
    }
    
  14. Question 9
    Mid

    What is trailing closure syntax?

    • Swift
    Reveal answer

    Many functions in iOS accept multiple parameters where the final parameter is a closure. Trailing closure syntax is a little piece of syntactic sugar that makes particularly common code more pleasant to read and write. Consider:

    func greetThenRunClosure(name: String, closure: () -> ()) {
      print("Hello, \(name)!")
      closure()
    }
    Use:
    greetThenRunClosure(name: "Paul") {
      print("The closure was run")
    }
    

    This functionality is available wherever a closure is the final parameter to a function.

  15. Question 9
    Mid

    Why and how to use associated values in enums?

    • Swift
    Reveal answer

    Enums with associated values let you associate extra data with an enum case. This helps make them significantly more useful, because we can create gradations of cases rather than have them be absolute. Consider:

    enum Weather {
      case sunny
      case cloudy(coverage: Int)
      case windy(speed: Int)
      case rainy(chance: Int)
    }
    let london = Weather.cloudy(coverage: 90)
    let gusty = Weather.windy(speed: 10)
    let guaranteedRain = Weather.rainy(chance: 100)
    
  16. Question 10
    Mid

    What is typecasting?

    • Swift
    Reveal answer

    Sometimes you know information that Swift doesn’t: you know that the UIViewController you have a reference to is in fact your custom PictureViewController subclass, and you want to treat it like one. As an example, here’s how we create new view controllers from a storyboard:

    let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewController
    

    That as? is a typecast - we’re passing on to Swift the knowledge that the view controller we expect to be created is in fact a DetailViewController . If the typecast fails then vc will be nil, but if it succeeds then Swift will be able to use it as a DetailViewController .

  17. Question 10
    Mid

    What’s the difference between a protocol and a class in Swift?

    • Swift
    Reveal answer

    In their basic form, a protocol describes what an unknown type of object can do. You might say it has two or three properties of various types, plus methods. But that protocol never includes anything inside the methods, or provides actual storage for the properties. In a more advanced form, you can write extensions on your protocols that provide default implementations of the methods. You still can’t provide storage for properties, however. In comparison, classes are concrete things. While they might adopt protocols – i.e., say they implement the required properties and methods – they aren’t required to do that. You can create objects from classes, whereas protocols are just type definitions. Try to think of protocols as being abstract definitions, whereas classes and structs are real things you can create.

  18. Question 11
    Mid

    When to use a set rather than an array in Swift?

    • Swift
    Reveal answer

    You should use a set rather than an array if all the following criteria are true:

    1. You intend to add each item only once. Sets never allow duplicates.
    2. You don’t care about the order of the items in the set.
    3. You don’t need to use APIs that require arrays.
    4. You’re storing Hashable types, either your own or one of Swift’s built-in types likes strings and integers. Sets use hash values for fast look up of items.
  19. Question 11
    Mid

    What does the Swift mutating keyword mean?

    • Swift
    Reveal answer

    Being the value type structs are immutable. Meaning other variables can not change the values for instance of structure at any given point. The mutating word is required for changing the values of self variables inside structure's function only. For. e.g

    struct MyStruct {
      var abc: String = "initila value"
      func changeValue() {
        abc = "some other value".
        //Compile time error: Cannot assign to property: 'self' is immutable.
    

    Mark method 'mutating' to make 'self' mutable.

    }
    }
    

    Here as we are trying to change value of variable abc inside function declared in struct itself, we get the compile time error. So here we need to make function mutating to make change value inside structure. Hence the correct code will be:

    struct MyStruct {
      var abc: String = "initila value"
      mutating func changeValue() {
        abc = "some other value"
      }
    }
    
  20. Question 12
    Mid

    What's the difference between Self vs self ?

    • Swift
    Reveal answer

    When you’re writing protocols and protocol extensions, there’s a difference between Self (capital S) and self (lowercase S). When used with a capital S, Self refers to the type that conform to the protocol, e.g. String or Int . When used with a lowercase S, self refers to the value inside that type, e.g. “hello” or 556. As an example, consider this extension on BinaryInteger :

    extension BinaryInteger {
      func squared() -> Self {
    

    return self * self

    }
    }
    

    Remember, Self with a capital S refers to whatever type is conforming to the protocol. In the example above, Int conforms to BinaryInteger , so when called on Int the method returns an Int . On the other hand, self with a lowercase S refers to whatever value the type holds. If the example above were called on an Int storing the value 5 it would effectively be 5 * 5 .

  21. Question 12
    Mid

    How to sort array of custom objects by property value in Swift?

    • Swift
    Reveal answer

    For in-place sorting consider:

    var images : [imageFile] = []
    images.sorted(by: { $0.fileID > $1.fileID })
    // swift 3+
    images.sorted { $0.fileID < $1.fileID }
    // exploiting a trailing closure
    

    If you are going to do this repeatedly and prefer to define a function, one way is: func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool { return this.fileID > that.fileID

    }
    ...
    images.sort(by: sorterForFileIDASC)
    
  22. Question 13
    Mid

    What does the required keyword in Swift mean?

    • Swift
    Reveal answer

    The required keyword means that inheriting classes must provide an implementation of the method. You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer. Whenever your class requires a certain setup when subclassed, mark the initializer as required . This makes sure that any new subclass instances will add support for that kind of usage.

  23. Question 14
    Mid

    What Classes and Structs have in common in Swift and what are their differences?

    • Swift
    Reveal answer

    The features classes and structs have in common: Both structs and classes can define properties to store values, and they can define functions They can define subscripts to provide access to values with subscript syntax They can define initializers to set up their initial state, with init() They can be extended with extension (this is important!) They can conform to protocols, for example to support Protocol Oriented Programming They can work with generics to provide flexible and reusable types Classes support a few more capabilities that structs don’t have: Classes can inherit from another class, like you inherit from UIViewController to create your own view controller subclass Classes can be deinitialized, i.e. you can invoke a deinit() function before the class is destroyed Classes are reference types and structs are value types Value Type: When you copy a value type (i.e., when it’s assigned, initialized or passed into a function), each instance keeps a unique copy of the data. If you change one instance, the other doesn’t change too. Reference Type: When you copy a reference type, each instance shares the data. The reference itself is copied, but not the data it references. When you change one, the other changes too.

  24. Question 14
    Mid

    What are the differences between functions and methods in Swift?

    • Swift
    Reveal answer

    Functions are self-contained chunks of code that perform a specific task. You give a function a name that identifies what it does, and this name is used to “call” the function to perform its task when needed. With functions, there is no self. Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. The method can access self .

    func someFunc{
      //some code
    }
    class someClass{
      func someMethod{
        //some code
      }
    }
    
  25. Question 15
    Mid

    When would you use self in a method?

    • Swift
    Reveal answer

    By far the most common reason for using self is inside an initializer, where your likely to want parameter names that match the property names of your type, like this:

    struct Student {
      var name: String
      var bestFriend: String
      init(name: String, bestFriend: String) {
        print("Enrolling \(name) in class…")
        self.name = name
        self.bestFriend = bestFriend
      }
    }
    
  26. Question 17
    Mid

    What is difference between as? , as! and as in Swift?

    • Swift
    Reveal answer

    In Swift 1.2 and later, as can only be used for

    upcasting (or disambiguation) and
    pattern matching:// 'as' for pattern matching
    switch item {
      case let obj as MyObject:
      // this code will be executed if item is of type MyObject
      case let other as SomethingElse:
      // this code will be executed if item is of type SomethingElse
      ...
    }
    

    as? produces an optional value, which is either the value if it can be cast to the specified type, or nil if it can't. as! doesn't produce an optional, it just produces a value of the specified type, and if the cast fails, it aborts the program. Saying foo as! SomeType is basically the same thing as saying (foo as? SomeType)! (except you get a better error message). You should only ever use as! if you're 100% certain the cast will succeed (because if you're wrong, the whole program aborts).

  27. Question 19
    Mid

    What is a good use case for an inout parameter?

    • Swift
    Reveal answer

    Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. inout means that modifying the local variable will also modify the passed-in parameters. Without it, the passed- in parameters will remain the same value. Trying to think of reference type when you are using inout and value type without using it.

    1. A good use case will be swap function that it will modify the passed-in parameters.
    2. Consider removing the overhead of copying as well. If you have a function that takes a somewhat memory-wise large value type as argument (say, a large structure type) and that returns the same type, and finally where the function return is always used just to replace the caller argument, then inout is to prefer as associated function parameter.
    struct MyStruct {
      private var myInt: Int = 1// ... lots and lots of stored properties
      mutating func increaseMyInt() {
        myInt += 1
      }
    }
    
    /* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
    function property is mutated
    function returns a copy of mutated property to caller (copy 2) */
    func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
      myHugeStruct.increaseMyInt()
    

    return myHugeStruct }/* call-by-reference, no value copy overhead due to inout opimization */

    func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
      myHugeStruct.increaseMyInt()
    }
    var a = MyStruct()
    a = myFunc(a)
    // copy, copy: overhead
    myFuncWithLessCopyOverhead(&a)
    // call by reference: no memory reallocation
    
  28. Question 20
    Mid

    In Swift enumerations, what’s the difference between raw values and associated values?

    • Swift
    Reveal answer

    Raw values are used to associate constant (literal) values to enum cases. The value type is part of the enum type, and each enum case must specify a unique raw value (duplicate values are not allowed). The following example shows an enum with raw values of type Int:

    enum IntEnum : Int {
      case ONE = 1
      case TWO = 2
      case THREE = 3
    }
    

    An enum value can be converted to its raw value by using the rawValue property:

    var enumVar: IntEnum = IntEnum.TWO
    var rawValue: Int = enumVar.rawValue
    

    A raw value can be converted to an enum instance by using a dedicated initializer:

    var enumVar: IntEnum? = IntEnum(rawValue: 1)
    

    Associated values are used to associate arbitrary data to a specific enum case. Each enum case can have zero or more associated values, declared as a tuple in the case definition:

    enum AssociatedEnum {
      case EMPTY
      case WITH_INT(value: Int)
      case WITH_TUPLE(value: Int, text: String, data: [Float])
    }
    Whereas the type(s) associated to a case are part of the enum declaration, the associated value(s) are instance
    

    specific, meaning that an enum case can have different associated values for different enum instances.

Senior questions

25 questions
  1. Question 1
    Senior

    When to use fileprivate access modifier in Swift?

    • Swift
    Reveal answer

    fileprivate keyword restricts the use of an entity to its defining source file. You typically use fileprivate access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.

    // First.framework – A.swift
    internal struct A {
      fileprivate static let x: Int
    }
    A.x
    // ok
    // First.framework – B.swift
    A.x
    // error: x is not available
    
  2. Question 2
    Senior

    What is Copy on Write (Cow) in Swift?

    • Swift
    Reveal answer

    Copy on Write (CoW or COW), sometimes referred to as implicit sharing or shadowing is a resource- management technique used in computer programming to efficiently implement a “duplicate” or “copy” operation on modifiable resources. If a resource duplicated but not modified it is not necessary to create a new resource. The resource can be shared between the copy and the original. At a basic level, Array is just a structure that holds a reference to a heap-allocated buffer containing the elements – therefore multiple Array instances can reference the same buffer. When you come to mutate a given array instance, the implementation will check if the buffer is uniquely referenced, and if so, mutate it directly. Otherwise, the array will perform a copy of the underlying buffer in order to preserve value semantics. Copy on write is a feature specifically added to Swift arrays and dictionaries; you don't get it for free in your own data types.

  3. Question 3
    Senior

    When is it safe to use an unowned reference?

    • Swift
    Reveal answer

    An unowned reference is assumed to always have a value. So you should only use it when you are sure that the reference will never be nil . Suppose we have two classes Customer and CreditCard . A customer can exist without a credit card, but a credit card will not exist without a customer, i.e. it can be assumed that a credit card will always have a customer. So, they should have the following relationship:

    class Customer {
      var card: CreditCard?
    }
    class CreditCard {
      unowned let customer: Customer
    }
    
  4. Question 4
    Senior

    Explain the difference between weak and unowned references. Provide an example.

    • Swift
    Reveal answer

    Both weak and unowned references do not create a strong hold on the referred object (a.k.a. they don't increase the retain count in order to prevent ARC from deallocating the referred object). A weak reference allows the possibility of it to become nil (this happens automatically when the referenced object is deallocated), therefore the type of your property must be optional - so you, as a programmer, are obligated to check it before you use it (basically the compiler forces you, as much as it can, to write safe code). Used for scenarios when _both entities can exis_t without having a strict dependency upon the other.

    class Person {
      let name: String
      init(name: String) { self.name = name }
      var apartment: Apartment?
    }
    class Apartment {
      let number: Int
      init(number: Int) { self.number = number }
      weak var tenant: Person?
    }
    

    or

    Person ===(strong)==> Apartment
    Person <==(weak)===== Apartment
    

    The Person and Apartment example shows a situation where two properties, both of which are allowed to be nil , have the potential to cause a strong reference cycle and can exist without having a strict dependency upon the other. An unowned reference presumes that it will never become nil during its lifetime. An unowned reference must be set during initialization - this means that the reference will be defined as a non-optional type that can be used safely without checks. If somehow the object being referred to is deallocated, then the app will crash when the unowned reference is used.

    class Customer {
      let name: String
      var card: CreditCard?
      init(name: String) { self.name = name }
    }
    class CreditCard {
      let number: UInt64
      unowned let customer: Customer
      init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer }
    }
    

    In this example, a Customer may or may not have a CreditCard , but a CreditCard will always be associated with a Customer .

  5. Question 5
    Senior

    When should you use Classes over Structs?

    • Swift
    Reveal answer

    Classes have a few extra characteristics that structs don’t have: Classes can inherit from another class, which you can’t do with structs. With classes, you can write class MyViewController : UIViewController to create a subclass of UIViewController . Structs can implement protocols, can be extended, and can work with generics, though but with classes, you can clearly define a parent-child connection between subclass and superclass. Classes can be deinitialized, i.e. they can implement a deinit function. Also, you can make one or more references to the same class (i.e., classes are a reference type). Classes come with the built-in notion of identity, because they’re reference types. With the identity operator === you can check if two references (variables, constants, properties, etc.) refer to the same object. It’s important to note here that you need to use classes if you want to interop between Swift and Objective-C.

  6. Question 6
    Senior

    When should you use Structs over Classes?

    • Swift
    Reveal answer

    It’s recommended to use struct in Swift by default. Structs are helpful in these scenarios, too: Simple Data Types Use structs for simple data types. Think about database objects you want to pass around in your code, like NewsItem , Task or User . Since they’re so well-defined, and often don’t need to accommodate complex relationships between objects, it’s simpler to use structs. Thread Safety In a multi-threaded environment, for instance with a database connection that’s opened in a different thread, structs are safer. They can be copied from one thread to another thread, without running the risk of a race condition or deadlock. Classes do not have this inherent safety, unless they’re deliberately made thread-safe. Mostly Structs Scenario When the properties of a struct are mostly value types too, like String , it makes sense to wrap them in a struct instead of a class. It’s OK to use structs within class types, but pay extra attention if you use classes within struct types. Classes are reference types, so if you’re unaware that your struct references a shared class instance, and your struct gets copied, both structs share a reference to that class! Don’t Need Inheritance Structs cannot inherit code from other structs. If you don’t need inheritance, it’s smarter to use structs

    (instead of classes). Structs can still conform to protocols, and they can be extended, so you still have a few options for creating flexible, composable code. Using structs has a huge benefit: it’s easier to reason about data changes in your code. When a type is a struct, you can be certain that no other part of your code can hold on to a reference to that struct.

  7. Question 8
    Senior

    Is there a way to create an abstract class in Swift?

    • Swift
    Reveal answer

    There are no abstract classes in Swift. You can achieve the same behaviour as abstract class with protocols and protocol extensions. First, you write a protocol that acts as an interface for all the methods that have to be implemented in all types that conform to it.

    protocol Drivable {
      var speed: Float { get set }
    }
    

    Then you can add default behaviour to all types that conform to it

    extension Drivable {
      func accelerate(by: Float) {
        speed += by
      }
    }
    

    You can now create new types by implementing Drivable .

    struct Car: Drivable {
      var speed: Float = 0.0
      init() {}
    }
    let c = Car()
    c.accelerate(10)
    

    So basically you get:

    1. Compile time checks that guarantee that all Drivable s implement speed

    2. You can implement default-behaviour for all types that conform to Drivable ( accelerate )

    3. Drivable is guaranteed not to be instantiated since it's just a protocol

  8. Question 8
    Senior

    Explain when to use different Swift casting operators?

    • Swift
    Reveal answer

    The as operator The as operator performs a cast when it is known at compile time that the cast always succeeds, such as upcasting or bridging. Upcasting lets you use an expression as an instance of its type’s supertype, without using an intermediate variable. This is the most preferable operator to use, when possible. It guarentees success, without worrying about unwrapping an optional or risking a crash. The as? operator The as? operator performs a conditional cast of the expression to the specified type. The as? operator returns an optional of the specified type. At runtime, if the cast succeeds, the value of expression is wrapped in an optional and returned; otherwise, the value returned is nil . If casting to the specified type is guaranteed to fail or is guaranteed to succeed, a compile-time error is raised. This is the second most preferable operator to use. Use it to safely handle the case in which a casting operator can't be performed.

    The as! operator The as! operator performs a forced cast of the expression to the specified type. The as! operator returns a value of the specified type, not an optional type. If the cast fails, a runtime error is raised. The behavior of x as! T is the same as the behavior of (x as? T)! . This is the least preferable operator to use. I strongly advise against abusing it. Attempting to cast an expression to an incompatible type crashes your program.

  9. Question 9
    Senior

    How to add a custom initializer to a struct without losing its memberwise initializer?

    • Swift
    Reveal answer

    All structs in Swift come with a default memberwise initializer, which is an initializer that accepts values for all the properties in the struct. However, as soon as you add your own initializer to the struct that memberwise initializer goes away, because it’s possible you’re doing special work that the default initializer isn’t aware of. If you want to keep both the default initializer and your own custom ones, there’s a simple trick: create your initializers inside an extension rather than as part of the main struct definition. For example:

    struct Person {
      var firstName: String
      var lastName: String
    }
    extension Person {
      init(name: String) {
        let split = name.components(separatedBy: " ")
        firstName = split.first ?? ""
        lastName = split.last ?? ""
      }
    }
    

    Because my custom initializer is inside an extension, you can create instances of Person in two ways:

    let taylor1 = Person(firstName: "Taylor", lastName: "Swift")
    let taylor2 = Person(name: "Taylor Swift")
    
  10. Question 10
    Senior

    When should you use Structs over Classes?

    • Swift
    Reveal answer

    It’s recommended to use struct in Swift by default. Structs are helpful in these scenarios, too: Simple Data Types Use structs for simple data types. Think about database objects you want to pass around in your code, like NewsItem , Task or User . Since they’re so well-defined, and often don’t need to accommodate complex relationships between objects, it’s simpler to use structs. Thread Safety In a multi-threaded environment, for instance with a database connection that’s opened in a different thread, structs are safer. They can be copied from one thread to another thread, without running the risk of a race condition or deadlock. Classes do not have this inherent safety, unless they’re deliberately made thread-safe. Mostly Structs Scenario When the properties of a struct are mostly value types too, like String , it makes sense to wrap them in a

    struct instead of a class. It’s OK to use structs within class types, but pay extra attention if you use classes within struct types. Classes are reference types, so if you’re unaware that your struct references a shared class instance, and your struct gets copied, both structs share a reference to that class! Don’t Need Inheritance Structs cannot inherit code from other structs. If you don’t need inheritance, it’s smarter to use structs (instead of classes). Structs can still conform to protocols, and they can be extended, so you still have a few options for creating flexible, composable code. Using structs has a huge benefit: it’s easier to reason about data changes in your code. When a type is a struct, you can be certain that no other part of your code can hold on to a reference to that struct.

  11. Question 10
    Senior

    What to use: [String: Any] or [String: AnyObject] ?

    • Swift
    Reveal answer

    Whether you use Any or AnyObject depends on your intended use: If your dictionary will be used only within Swift code, then you should use Any because your types ( Int , Double , Float , String , Array , and Dictionary ) are not objects. If you will be passing your dictionary to Objective-C routines that expect an NSDictionary , then you should use AnyObject . When you import Foundation or import UIKit or import Cocoa , it is possible to declare your array as [String: AnyObject] , but in this case Swift is treating your Int , Double , Float literals as NSNumber , your String s as NSString , your Array s as NSArray , and your dictionaries as NSDictionary , all of which are objects. A dictionary using AnyObject as the value type is convertible to NSDictionary , but one using Any is not.

  12. Question 11
    Senior

    What’s the difference between Any and AnyObject ?

    • Swift
    Reveal answer

    Swift provides two special types for working with nonspecific types: Any can represent an instance of any type at all (both value and reference types), including function types. AnyObject can represent an instance of any class type (reference types). Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.

  13. Question 11
    Senior

    Explain what will be the output of this code?

    • Swift
    Reveal answer

    Problem

    Consider:

    func printStringNumbers() {
      defer { print("1") }
      defer { print("2") }
      defer { print("3") }
      print("4")
    }
    

    What will be the output of this code?

    Solution

    A defer statement is used for executing code just before transferring program control outside of the scope that the statement appears in. If multiple `defer statements appear in the same scope, the order they appear is the reverse of the order they are executed. 4, 3, 2, 1

  14. Question 12
    Senior

    How to add a custom initializer to a struct without losing its memberwise initializer?

    • Swift
    Reveal answer

    All structs in Swift come with a default memberwise initializer, which is an initializer that accepts values for all the properties in the struct. However, as soon as you add your own initializer to the struct that memberwise initializer goes away, because it’s possible you’re doing special work that the default initializer isn’t aware of. If you want to keep both the default initializer and your own custom ones, there’s a simple trick: create your initializers inside an extension rather than as part of the main struct definition. For example:

    struct Person {
      var firstName: String
      var lastName: String
    }
    extension Person {
      init(name: String) {
        let split = name.components(separatedBy: " ")
        firstName = split.first ?? ""
        lastName = split.last ?? ""
      }
    }
    

    Because my custom initializer is inside an extension, you can create instances of Person in two ways:

    let taylor1 = Person(firstName: "Taylor", lastName: "Swift")
    let taylor2 = Person(name: "Taylor Swift")
    
  15. Question 13
    Senior

    Is there a way to create an abstract class in Swift?

    • Swift
    Reveal answer

    There are no abstract classes in Swift. You can achieve the same behaviour as abstract class with protocols and protocol extensions. First, you write a protocol that acts as an interface for all the methods that have to be implemented in all types that conform to it.

    protocol Drivable {
      var speed: Float { get set }
    }
    

    Then you can add default behaviour to all types that conform to it

    extension Drivable {
      func accelerate(by: Float) {
        speed += by
      }
    }
    

    You can now create new types by implementing Drivable .

    struct Car: Drivable {
      var speed: Float = 0.0
      init() {}
    }
    let c = Car()
    c.accelerate(10)
    

    So basically you get:

    1. Compile time checks that guarantee that all Drivable s implement speed
    2. You can implement default-behaviour for all types that conform to Drivable ( accelerate )
    3. Drivable is guaranteed not to be instantiated since it's just a protocol
  16. Question 14
    Senior

    Are there any differences between Protocol in Swift vs Interface in Java?

    • Java Swift
    Reveal answer

    Essentially protocols are very similar to Java interfaces except for: Swift protocols can also specify properties that must be implemented (i.e. fields) Swift protocols need to deal with value/reference through the use of the mutating keyword (because protocols can be implemented by structures, enumerations or classes). you can combine protocols at any point using "Protocol Composition". This replaces the older swift protocol<A, B> way of protocol composition. For example, declaring a function parameter that must adhere to protocol Named and Aged as: func wishHappyBirthday(to celebrator: Named & Aged) {}

  17. Question 15
    Senior

    What’s the difference between a static variable and a class variable?

    • Swift
    Reveal answer

    Both the static and class keywords allow us to attach variables to a class rather than to instances of a class. Where static and class differ is how they support inheritance: When you make a static property it becomes owned by the class and cannot be changed by subclasses, whereas when you use class it may be overridden if needed. Consider:

    class Person {
      static var count: Int {
    

    return 250

    }
    class var averageAge: Double {
    

    return 30

    }
    }
    class Student: Person {
      // THIS ISN'T ALLOWED
    
      // override static var count: Int {
        // return 150
        // }
      // THIS IS ALLOWED
      override class var averageAge: Double {
    

    return 19.5

    }
    }
    
  18. Question 16
    Senior

    Provide an example of code with a memory leak

    • Swift
    Reveal answer

    Consider:

    class A {
      var b: B!
      deinit {
        print("deinit of A")
      }
    }
    class B {
      var a: A!
      deinit {
        print("deinit of B")
      }
    }
    do {
      let a = A()
      let b = B()
      a.b = b
      b.a = a
    }
    If you run this code, it will print nothing. It means that deinit (and deallocation) never called for both objects
    

    and they just leaked. The A cannot be deallocated as long as the B is alive and the B cannot be deallocated as long as the A is alive. That situation is called Strong Reference Cycle and leads to memory leaks. But if you declare one of the properties as weak :

    class A {
      weak var b: B!
      deinit {
        print("deinit of A")
      }
    }
    

    Then deinit will be called and you'll see messages in console.

  19. Question 16
    Senior

    What is the difference between functions and closures?

    • Swift
    Reveal answer

    Roughly, a closure is a block of code that may capture variable values from its surrounding scope.

    let simpleClosure:(String) -> (String) = { name in
      let greeting = "Hello, World! " + "Program"
    

    return greeting

    }
    let result = simpleClosure("Hello, World")
    print(result)
    

    Roughly, a function is a statically defined block of code that may use variable values from its surrounding scope. Note that these are almost exactly the same thing, at least at a conceptual level. The difference is that functions are traditionally invoked in the scope where they are defined, whereas closures are invoked — potentially — far away from their point of definition. That's why functions use, but closures capture, the relevant parts of their surrounding environment. A closure is similar to a C-style callback, except that instead of needing a separate "context" parameter, it has its own context built in. In Swift, a function (including a method) is semantically just a closure with a static name — you can pass a function name anywhere that a closure parameter is required, assuming that the type signature is correct. The compiler takes care of smoothing over the difference between "use" and "capture".

  20. Question 17
    Senior

    What is the difference between ARC (automatic reference counting) and GC (garbage collection)?

    • Objective-C iOS Swift
    Reveal answer

    ARC rely on a compile time "referenced" objects which make it efficient in a low-power mode environments (Mobile devices). ARC injects a code into the executable to be executed "automatically" on unused objects depending on their reference count. Advantages: Real-time, deterministic destruction of objects as they become unused. No background processing. Disadvantages: ARC cannot handle retain cycles automatically. GC rely on a runtime based "reachable" objects which make it efficient in a multi-threaded environment. GC works in the runtime as it will detect the unused object graphs (will eliminate retain-cycles) and remove them on an indeterminate time intervals Advantages: GC can clean up entire object graphs, including retain cycles. GC proceed in the background, so less memory management work is done as part of the regular application flow. Disadvantages: Because GC happens in the background, the exact time frame for object releases is undetermined. When a GC happens, other threads in the application may be temporarily put on hold.

  21. Question 17
    Senior

    What is the difference between static func and class func in Swift?

    • Swift
    Reveal answer

    static func is same as final class func Consider:

    class ClassA {
      class func func1() -> String {
    

    return "func1"

    }
    static func func2() -> String {
    

    return "func2"

    }/* same as above
    final class func func2() -> String {
    

    return "func2"

    }
    */
    }
    

    Note because func2 is final , we can not override it in subclass as below:

    class ClassB : ClassA {
      override class func func1() -> String {
    

    return "func1 in ClassB"

    }// ERROR: Class method overrides a 'final` class method
    override static func func2() -> String {
    

    return "func2 in ClassB"

    }
    }
    

    Also check that:

  22. Question 18
    Senior

    What is the autoclosure attribute and when to use it?

    • Swift
    Reveal answer

    Swift’s @autoclosure attribute enables you to define an argument that automatically gets wrapped in a closure. It’s primarily used to defer execution of a (potentially expensive) expression to when it’s actually needed, rather than doing it directly when the argument is passed. As an usage example consider the conditional expression passed as the first parameter to until will be automatically wrapped up into a closure expression and can be called each time around the loop

    func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) {
      while !pred() {
        block()
      }
    }// doSomething until condition becomes true
    until(condition) {
      doSomething()
    }
    
  23. Question 18
    Senior

    What is the difference between fileprivate and private ?

    • Swift
    Reveal answer

    fileprivate can be accessed from the their entire files. Restricts the use of an entity to its defining source file. private can only be accessed from their single declaration and to extensions of that declaration that are in the same file; For instance:

    // Declaring "A" class that has the two types of "private" and "fileprivate":
    class A {
      private var aPrivate: String?
      fileprivate var aFileprivate: String?
      func accessMySelf() {
        // this works fine
        self.aPrivate = ""
    
        self.aFileprivate = ""
      }
    }// Declaring "B" for checking the abiltiy of accessing "A" class:
    class B {
      func accessA() {
        // create an instance of "A" class
        let aObject = A()
        // Error! this is NOT accessable...
        aObject.aPrivate = "I CANNOT set a value for it!"
        // this works fine
        aObject.aFileprivate = "I CAN set a value for it!"
      }
    }
    
  24. Question 19
    Senior

    What is QoS (Quality of Service) in GCD?

    • Swift iOS
    Reveal answer

    Defining quality of service helps us categorize the task of our DispatchQueue with priority. By using it the right way, we could make our app energy efficient and responsive. QoS can be used with .async() function, and the priorities are divided into four main categories. userInteractive: Used for animations, or updating UI. userInitiated: Used for tasks like loading data from API, preventing the user from making interactions. utility: Used for tasks that do not need to be tracked by the user. background: Used for tasks like saving data in the local database or any maintenance code which is not on high priority. Consider:

    let queue = DispatchQueue(label: "com.swiftpal.dispatch.qos")// async with background type
    queue.async(qos: .background) {
      print("Background Code")
    }// async with userInitiated type
    queue.async(qos: .userInitiated) {
      print("User Initiated Code")
    }
    
  25. Question 19
    Senior

    What is the difference between open and public keywords in Swift?

    • Swift
    Reveal answer

    In short: An open class is accessible and subclassable outside of the defining module. An open class member is accessible and overridable outside of the defining module. The reasoning there is some classes of libraries and frameworks are not designed to be subclassed and doing so may result in unexpected behavior. A public class is accessible but not subclassable outside of the defining module. A public class member is accessible but not overridable outside of the defining module.

    // First.framework – A.swift
    open class A {}
    // First.framework – B.swift
    public class B: A {}
    // ok
    // Second.framework – C.swift
    import First
    internal class C: A {}
    // ok
    // Second.framework – D.swift
    import First
    internal class D: B {}
    // error: B cannot be subclassed
    

Expert questions

13 questions
  1. Question 2
    Expert

    What is the use of Hashable protocol?

    • Swift
    Reveal answer

    In Swift, the Hashable protocol is the main component used to provide hashing capabilities to custom types. It is most commonly used to allow types to be used as the key of Dictionaries and Sets . If an object conforms to the hashable protocol, it needs to have a hashValue . The hashValue can be used to compare objects / uniquely identify the object. You can compare objects in two ways:

    1. === function. This checks object references (can only be used with classes). It checks if the left object has the same reference to the right object. Even if both objects have exactly the same property values BUT they do have a different reference, it returns false.
    2. == function ( Equatable protocol). It checks if the objects are equal to eachother based on the static func == . You can return the hashValue of the object. In that way, you can say objects are equal to eachtoher based on the properties, rather than reference.

    If you provide your own hashValue , you can say objects are equal to eachother in a way you say objects are equal to eachother, regardless of the reference to the object. You can use objects in a Set that conform to the hashable protocol, because a Set checks if objects are equal to eachother based on the hashValue .

  2. Question 3
    Expert

    What’s the difference between init?() and init() ?

    • Swift
    Reveal answer

    init?() is a failable initializer and can return nil rather than a value if something goes wrong during creation. Consider:

    struct Person {
      var ssn: String
      init?(socialSecurityNumber: String) {
        if socialSecurityNumber.count < 11 {
    

    return nil

    } else {
      self.ssn = socialSecurityNumber
    }
    }
    }
    The initializer is now called init?() to reflect that it returns an optional – the process might return nil if the
    creation fails.
    
  3. Question 4
    Expert

    What is the Never return type? When to use it over Void ?

    • Swift
    Reveal answer

    Void Void is itself a return type which is a tuple with zero elements. You can use Void and () interchangeably. Look at these examples,

    1. func yourFunc() {} This is a function without a return type, which basically returns a tuple with zero elements, that can be written as ()
    2. func yourFunc() -> Void {} Function which is explicitly informing the compiler about return type of void
    3. func yourFunc() -> () {} This return type of () displays the same as void type. () indicates a tuple with zero elements Never Never return-type informs the compiler that no need exists to return an empty tuple (). Also, function with the never return type is used for the exit point of the current execution like a crash, fatal error, abort or exit. Consider:
    public func abortVoid() -> Void {
      fatalError()
    }
    public func abortNever() -> Never {
      fatalError()
    }
    func bar1() -> Int {
      if true {
        abortVoid()
        // ERROR: Missing return in a function expected to return 'Int'
      } else {
    

    return 1

    }
    }
    func bar2() -> Int {
      if true {
        abort()
        // No ERROR, but compiler sees it returns Never and warns:
    

    return 2

    // Will never be executed
    } else {
    

    return 1

    }
    }
    
  4. Question 5
    Expert

    How to deal with retain cycles in Closures? Provide an example.

    • Swift
    Reveal answer

    You always need to be mindful of memory management when using closures. Like classes, closures arereference types. Because a closure can capture values from its surrounding scope, a closure can introduce a reference cycle. Consider:

    class C {
      var f: (Void -> Void)!
      deinit {
        print("deinit for C")
      }
    }
    do {
      let c = C()
      c.f = {
        print(c)
      }
    }
    

    c captures f , f captures c . So we got memory leak. To deal with leaks in closures, you have 2 options – declare that captured object is weak or unowned . Like this:

    do {
      let c = C()
      c.f = { [weak c] in
    
        print(c)
      }
    }
    

    Basically, you would use weak if it's possible for object to get out of existence and become nil when closure is called; but if you are sure object will still exist at this time, use unowned instead.

  5. Question 7
    Expert

    Why can not we just use weak everywhere and forget about unowned ?

    • Swift
    Reveal answer

    A weak reference is more accurately named an auto-zeroing weak reference. The system knows about an object stored in a weak variable, and when the object is deallocated, goes to the extra work to zero out the

    value. As a consequence of that, weak variables are optionals in Swift. Unowned pointers are not memory managed at all. They are a raw memory pointer that can take any value. (They can still be typed to point to a particular type of structure, so they aren't always "raw" in the way a C void pointer is raw.) Unowned pointers are useful for pointing to malloc'ed memory, pointer arithmetic, and the like. Declaring a pointer type as unowned basically tells the compiler "Don't worry about it. I know what I'm doing." The burden is then on you to make sure the pointer is always valid, before you try to dereference it, and also manage the memory behind the pointer yourself.

  6. Question 8
    Expert

    Explain the use case when ARC won't help you to release memory (but GC will)?

    • Objective-C iOS Swift
    Reveal answer

    As an example, if you have a class named node, and node has an NSArray of children, and a single reference to its parent that "just works" with GC. With ARC (and manual reference counting as well) you have a problem. Any given node will be referenced from its children and also from its parent. Like:

    A -> [B1, B2, B3]
    B1 -> A, B2 -> A, B3 -> A
    All is fine while you are using A (say via a local variable).
    

    When you are done with it (and B1/B2/B3), a GC system will eventually decide to look at everything it can find starting from the stack and CPU registers. It will never find A,B1,B2,B3 so it will finalize them and recycle the memory into other objects. When you use ARC or MRC, and finish with A it have a refcount of 3 (B1, B2, and B3 all reference it), and B1/B2/B3 will all have a reference count of 1 (A's NSArray holds one reference to each). So all of those objects remain live even though nothing can ever use them. The common solution is to decide one of those references needs to be weak (not contribute to the reference count). That will work for some usage patterns, for example if you reference B1/B2/B3 only via A. However in other patterns it fails. For example if you will sometimes hold onto B1, and expect to climb back up via the parent pointer and find A. With a weak reference if you only hold onto B1, A can (and normally will) evaporate, and take B2, and B3 with it. You can also say ARC puts a bigger priority on performance (or maybe predictability) while GC puts a bigger priority on being a generic solution. As a result GC has less predictable CPU/memory demands, and a lower

    performance (normally) than ARC, but can handle any usage pattern. ARC will work much better for many many
    common usage patterns, but for a few (valid!) usage patterns it will fall over and die.
    
  7. Question 9
    Expert

    Explain what is DispatchGroup?

    • Swift
    Reveal answer

    DispatchGroup allows for aggregate synchronization of work. You can use them to submit multiple different work items and track when they all complete, even though they might run on different queues. This behavior can be helpful when progress can’t be made until all of the specified tasks are complete. A scenario when this could be useful is if you have multiple webservice calls that all need to finish before continuing. For example, you need to download multiple sets of data that needs to be processed by some function. You have to wait for all webservices to complete before calling the function to process all the received data.

    func doLongTasksAndWait () {
      print("starting long running tasks")
      let group = DispatchGroup()
      //create a group for a bunch of tasks we are about to do
      for i in 0...3 {
        //launch a bunch of tasks (eg a bunch of webservice calls that
        all need to be finished before proceeding to the next ViewController)
        group.enter()
        //let the group know that something is being added
        DispatchQueue.global().async {
          //run tasks on a background thread
          sleep(arc4random() % 4)
          //do some long task eg webservice or database lookup (here we
          are just sleeping for a random amount of time for demonstration purposes)
          print("long task \(i) done!")
          group.leave()
          //let group know that the task is finished
        }
    }
    group.wait()
    //will block whatever thread we are on here until all the above
    tasks have finished (so maybe dont use this function on your main thread)
    print("all tasks done!")
    }
    

    Alternatively, if you do not want to wait for the groups to finish, but instead want to run a function once all the tasks have completed, use the notify function in place of the group.wait()

    group.notify(queue: DispatchQueue.main) {
      //the queue: parameter is which queue this block will run on,
    

    if you need to do UI updates, use the main queue

    print("all tasks done!")
    //this will execute when all tasks have left the group
    }
    
  8. Question 10
    Expert

    What are the benefits of using DispatchWorkItem in Swift?

    • Swift iOS
    Reveal answer

    The DispatchWorkItem class is an encapsulation of the concept of work item. By encapsulating our request code in a work item, we can very easily cancel it whenever it's replaced by a new one, like this:

    class SearchViewController: UIViewController, UISearchBarDelegate {
      // We keep track of the pending work item as a property
      private var pendingRequestWorkItem: DispatchWorkItem?
      func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        // Cancel the currently pending item
        pendingRequestWorkItem?.cancel()
        // Wrap our request in a work item
        let requestWorkItem = DispatchWorkItem { [weak self] in
          self?.resultsLoader.loadResults(forQuery: searchText)
        }
      // Save the new work item and execute it after 250 ms
      pendingRequestWorkItem = requestWorkItem
    
      DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250),
      execute: requestWorkItem)
    }
    }
    
  9. Question 11
    Expert

    Explain usage of Concurrent vs Serial Queues with async and sync blocks

    • iOS Swift
    Reveal answer
    1. the queue's type ( Concurrent or Serial) is the ONLY element that decides whether the tasks are executed in order or in paralel;
    2. the dispatch type ( sync or async ) is only saying whether the execution goes OR doesn't go to the next instruction The queue dictates the execution policy, not how you queue the block. sync waits for the block to complete, async doesn't. A simple example: you have a block that takes a minute to execute. You add it to a queue from the main thread. Let's look at the four cases. async - concurrent: the code runs on a background thread. Control returns immediately to the main thread (and UI). The block can't assume that it's the only block running on that queue async - serial: the code runs on a background thread. Control returns immediately to the main thread. The block can assume that it's the only block running on that queue sync - concurrent: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can't assume that it's the only block running on that queue (I could have added another block using async a few seconds previously) sync - serial: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can assume that it's the only block running on that queue
  10. Question 12
    Expert

    Explain the use case when ARC won't help you to release memory (but GC will)?

    • Objective-C iOS Swift
    Reveal answer

    As an example, if you have a class named node, and node has an NSArray of children, and a single reference to its parent that "just works" with GC. With ARC (and manual reference counting as well) you have a problem. Any given node will be referenced from its children and also from its parent. Like:

    A -> [B1, B2, B3]
    B1 -> A, B2 -> A, B3 -> A
    
    All is fine while you are using A (say via a local variable).
    

    When you are done with it (and B1/B2/B3), a GC system will eventually decide to look at everything it can find starting from the stack and CPU registers. It will never find A,B1,B2,B3 so it will finalize them and recycle the memory into other objects. When you use ARC or MRC, and finish with A it have a refcount of 3 (B1, B2, and B3 all reference it), and B1/B2/B3 will all have a reference count of 1 (A's NSArray holds one reference to each). So all of those objects remain live even though nothing can ever use them. The common solution is to decide one of those references needs to be weak (not contribute to the reference count). That will work for some usage patterns, for example if you reference B1/B2/B3 only via A. However in other patterns it fails. For example if you will sometimes hold onto B1, and expect to climb back up via the parent pointer and find A. With a weak reference if you only hold onto B1, A can (and normally will) evaporate, and take B2, and B3 with it. You can also say ARC puts a bigger priority on performance (or maybe predictability) while GC puts a bigger priority on being a generic solution. As a result GC has less predictable CPU/memory demands, and a lower

    performance (normally) than ARC, but can handle any usage pattern. ARC will work much better for many many
    common usage patterns, but for a few (valid!) usage patterns it will fall over and die.
    
  11. Question 12
    Expert

    What is the difference between @escaping and @nonescaping Closures in Swift?

    • Swift
    Reveal answer

    A non-escaping closure is a closure that is called within the function it is passed into. The execution of the closure will be before the function returns Pass the closure as a function argument The function processes some work The function runs the closure The function returns

    let add: (Int,Int) -> () = { a,b in
      print("Result \(a + b)")
    }
    func mathForOneAndTwo(firstNum: Int, secondNum: Int, action: (Int,Int) -> Void) {
      action(firstNum,secondNum)
    
    }
    mathForOneAndTwo(firstNum: 1, secondNum: 2, action: add)
    

    An escaping closure is a closure that executes after the function it is passed into returns. The closure outlives the function that it is passed into, and this is known as escaping. escaping closures are frequently used for asynchronous execution or storage. Pass the closure as a function argument The function processes some work The function returns At some later stage, the closure is executed

    let add: (Int,Int) -> () = { a, b in
      print("Result \(a + b)")
    }
    func mathForOneAndTwo(firstNum: Int, secondNum: Int, action: @escaping (Int,Int) -> Void) {
      DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: {
        action(firstNum,secondNum)
      } )
    }
    mathForOneAndTwo(firstNum: 1, secondNum: 2, action: add)
    

    Closures themselves are reference types, and by default they are non-escaping closures.

  12. Question 13
    Expert

    Explain usage of Concurrent vs Serial Queues with async and sync blocks

    • iOS Swift
    Reveal answer
    1. the queue's type ( Concurrent or Serial) is the ONLY element that decides whether the tasks are executed in order or in paralel;
    2. the dispatch type ( sync or async ) is only saying whether the execution goes OR doesn't go to the next instruction The queue dictates the execution policy, not how you queue the block. sync waits for the block to complete, async doesn't. A simple example: you have a block that takes a minute to execute. You add it to a queue from the main thread. Let's look at the four cases. async - concurrent: the code runs on a background thread. Control returns immediately to the main thread (and UI). The block can't assume that it's the only block running on that queue async - serial: the code runs on a background thread. Control returns immediately to the main thread. The block can assume that it's the only block running on that queue sync - concurrent: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can't assume that it's the only block running on that queue (I could have added another block using async a few seconds previously) sync - serial: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can assume that it's the only block running on that queue
  13. Question 20
    Expert

    What's the difference between marking a method as @objc vs dynamic , when would you do one vs the other?

    • Objective-C Swift
    Reveal answer

    A function/variable declared as @objc is accessible from Objective-C, but Swift will continue to access it directly via static or virtual dispatch. This means if the function/variable is swizzled via the Objective-C framework, like what happens when using Key-Value Observing or the various Objective-C APIs to modify classes, calling the method from Swift and Objective-C will produce different results. Using dynamic tells Swift to always refer to Objective-C dynamic dispatch. This is required for things like Key- Value Observing to work correctly. When the Swift function is called, it refers to the Objective-C runtime to dynamically dispatch the call.