iOS Development Q & A

2022/04/12

iOS Development Q & A

Below is a summary of notes taken during my Swift/iOS development study.

TOC

Data Structure

Q: What are the commonalities and differences between struct and class?

A: struct and class have the following features in common:

They also have the following differences:

struct:

class:

Stack and Heap:

Q: What are the common use cases of struct and class?

A:

struct:

class:

Q: What do you need to do, to have a mutable struct?

A: Mark methods that change the internal state as mutating; Also, the variable of this struct type need to be var instead of let.

Q: Can you name two benefits of using subclassing instead of enums with associated types?

A: A superclass prevents duplication; no need to declare the same property twice. With subclassing, you can also override existing functionality.

Q: Can you name two benefits of using enums with associated types instead of subclassing?

A: No need to refactor anything if you add another type, whereas with subclassing you risk refactoring a superclass and its existing subclasses. Second, you’re not forced to use classes.

Q: Which raw types are supported by enums?

A: String, character, and integer and floating-point types.

Q: Are an enum’s raw values set at compile time or runtime?

A: Raw type values are determined at compile time.

Q: Are an enum’s associated values set a compile time or runtime?

A: Associated values are set at runtime.

Q: Which types can go inside an associated value for enum?

A: All types fit inside an associated value.

Q: How can enums references themselves as associated value?

A: You can use indirect enum, they are called “indirect” because they modify the way Swift stores them so they can grow to any size. Without the indirection, any enum that referenced itself could potentially become infinitely sized: it could contain itself again and again, which wouldn’t be possible.

Q: How could you preserve a struct’s memberwise initializer while having your custom initializer?

A: Define your custom initializer in an extension.

Q: How could you list all cases of an enum?

A: Swift has a CaseIterable protocol that automatically generates an array property of all cases in an enum. To enable it, all you need to do is make your enum conform to the CaseIterable protocol and at compile time Swift will automatically generate an allCases property that is an array of all your enum’s cases, in the order you defined them.

Q: What is a tuple?

A: A tuple is a data structure like an anonymous struct, it can hold different types of data and it can be created on the fly. It’s commonly used to return multiple values from a function call. Tuples can be accessed using element names if provided, or using a position in the tuple, e.g. 0 and 1. While element names are not mandatory, it’s a good idea to have them, so the code is more readable. Why return a tuple instead of specific struct? When the return values are simple enough and not reused in different places, especially in cases like returning two elements from an array which doesn’t make sense to live in a specific struct.

Back to ToC

Memory Management

Q: What is ARC?

A: Automatic Reference Counting is a memory management method Swift uses to determine when objects should be deallocated. Each object has a reference count, and when the reference count reaches 0 the object is deallocated.

An object’s reference count is increased by 1 when a strong reference is assigned to that object. An object’s reference count is decreased by 1 when a strong reference is removed from that object.

Q: What is retain cycle?

A: It’s possible to write code in which an instance of a class never gets to a point where it has zero strong references. This can happen if two class instances hold a strong reference to each other, such that each instance keeps the other alive. This is known as a strong reference cycle.

Q: What are the difference between weak and unowned references?

A:

A weak reference does not increment or decrement the reference count of an object. Since weak references do not increment the reference count of an object, a weak reference can be nil, meaning the object could be deallocated while the weak reference is still pointing to it.

Like a weak reference, an unowned reference does not increment or decrement the reference count of an object. However, unlike a weak reference, the program guarantees to the Swift compiler that an unowned reference will not be nil when it is accessed.

Q: Do UIView Animation Blocks Require Weak or Unowned Self?

A: Not all blocks require weak or unowned self to prevent retain cycles. UIView animation blocks run only once and are then deallocated. This means even strong references in animation blocks will be released when the block runs.

Q: Are weak and unowned only used with self inside closures?

A: No, you can indicate any property or variable declaration weak or unowned as long as it’s a reference type.

Q: What will the code print?

var thing = "day"

let closure = { [thing] in
  print("To\(thing)")
}

thing = "morrow"

closure()

A:

It’ll print: “Today”.

The capture list creates a copy of thing when you declare the closure. This means that captured value doesn’t change even if you assign a new value to thing.

If you omit the capture list in the closure, then the compiler uses a reference instead of a copy. Therefore, when you invoke the closure, it reflects any change to the variable.

Q: Are closure value or reference types?

A: Closures are reference types. Assigning a closure to a variable, copy the variable into another, it’s still the reference being copie with its capture list.

Q: How would you identify and resolve a retain cycle?

A: There are tools in Instruments that shows memory leaks, which can be a start point for debugging retain cycle; Also, Memory Graph Debugger can be used to visualize memory allocations, which helps identifying retain cycles; Once a retain cycle is identified, we need to decide what should be made weak in order to break the cycle.

Back to ToC

Optionals

Q: What is an optional:

A: Optional is a Swift feature that a variable of any type represent either a value or a lack of value. In Objective-C, similar idea is only available for reference types using the nil special value. Value types, such as int or float, do not have this ability.

Q: How do you assign an optional to a variable with a fallback value if the optional is nil?

A: Use nil-coalescing operator ??, it falls back to a default value, but also unwraps the optional if it does have a value.

Q: What is optional chaining?

A: When you need a value from an optional property that can also contain another optional property.

Q: When do we use optional chaining vs if let or guard?

A: Optional chaining is used when we do not really care if the operation fails.

Q: Can you describe how to convert an optional Boolean property, say, feature toggle, which can be enabled, disabled or not set, into an enum?

A:

enum FeatureToggle: RawRepresentable {
  case enabled
  case disabled
  case notSet

  init(rawValue: Bool?) {
    switch rawValue {
      case true?: self = .enabled
      case false?: self = .disabled
      default: self = .notSet
    }
  }

  var rawValue: Bool? {
    switch self {
      case .enabled: return true
      case .disabled: return false
      case .notSet: return nil
    }
  }
}

Q: When is force unwrapping acceptable?

A: Ideally force unwrapping should not be used, but sometimes it cannot be avoided because otherwise the app can end up in a bad state, e.g. the app can’t recover from a nil value:

Q: Possible cases when implicitly unwrapped optionals cannot be avoided?

A:

  1. When you cannot initialize a property that is not nil by nature at instantiation time. A typical example is an Interface Builder outlet, which always initializes after its owner. In this specific case — assuming it’s properly configured in Interface Builder — you’ve guaranteed that the outlet is non-nil before you use it.
  2. To solve the strong reference cycle problem, which is when two instances refer to each other and require a non-nil reference to the other instance. In such a case, you mark one side of the reference as unowned, while the other uses an implicitly unwrapped optional.

Q: If no optionals in a function are allowed to have a value, what would be a good tactic to make sure that all the optionals are filled?

A: Use guard to block optionals at the start of a function.

Q: If a number of functions are executed in different paths depending on the optionals inside them, what would be a correct approach to handle all these paths?

A: Put multiple optionals inside a tuple allows you to pattern match on them and take different paths in a function.

Q: What are good alternatives to implicit unwrapped optional?

A: Lazy properties or factories that are passed via an initializer.

Q: What are the various ways to unwrap an optional? How do they rate in terms of safety?

A: There are several ways:

var x : String? = "Test"

Forced unwrapping — unsafe.

let a: String = x!

Implicitly unwrapped variable declaration — unsafe in many cases.

var a = x!

Optional binding — safe.

if let a = x {
  print("x was successfully unwrapped and is = \(a)")
}

Optional chaining — safe.

let a = x?.count

Nil coalescing operator — safe.

let a = x ?? ""

guard statement — safe.

guard let a = x else {
  return
}

Optional pattern — safe.

if case let a? = x {
  print(a)
}

Q: What’s the difference between nil and .none?

A: There is no difference, as Optional.none (.none for short) and nil are equivalent. In fact, this statement outputs true:

nil == .none

The use of nil is more common and is the recommended convention.

Q: What happens when you call map on optionals?

A: Mapping on optionals lets you perform actions on the optional as if the optional were unwrapped, so inside map’s closure you don’t need to know or deal with optional, this saves you some boilerplate code for unwrapping with temporary variables manually. On top of this, you can also chain several mapping operations.

Back to ToC

Protocols and Generics

Q: What is Protocol Extension?

A: Protocol extension is to extend a protocol and provide default implementation of methods, so that classes that conform to the protocol will have the default implementation for free, and classes can selectively override protocol methods just like class inheritance. The benefit of this is that a class can have 0 or 1 superclass but protocols don’t have such constraint.

Q: Explain how a function with a generic type parameter works behind the scene.

A: Swift creates multiple functions behind the scenes for different types of the generic parameter types, this process is called monomorphization where the compiler turns polymorphic code into concrete singular code. Swift is clever enough to prevent all combinations of concrete parameter types that lead to large binaries. Swift uses several measures involving metadata to limit the amount of code generation. In the case of function with generic parameter type, the compiler creates a low-level function; For relevant types, Swift generates metadata, called value witness tables. At runtime, Swift passes the corresponding metadata to the low-level representation of generic parameter type when needed.

Q: What is invariance for Swift’s generics? Is there any exception?

A: Swift’s generics are invariant, meaning even if a generic type wraps a subclass, it does not make it a subtype of a generic wrapping its superclass. Invariance is a safe way to handle polymorphism. Swift’s builtin generic types, such as Array or Optional, do allow for subtyping with generics. e.g. where it expects Array<BaseClass>, it’s OK to pass an instance of type Array<SubClass>.

Q: Given the below protocol:

protocol SomeProtocol {}

What is the difference between the following statements?

func doWork(_ something: SomeProtocol) {}
func doWork<T: SomeProtocol>(_ something: T) {}

A: The nongeneric function uses dynamic dispatch (runtime), the generic function is resolved at compile time.

Q: What are the differences between using a protocol as a type, and using generics that conform to the protocol?

A: Using a protocol as a type speeds up programming and makes mixing and swapping things around easier. Generics are more restrictive and wordy, but they give you performance benefits and compile-time knowledge of the types you’re implementing.

Q: Is there any problem with the following code:

protocol Input {}
protocol Output {}
protocol Processor {
  @discardableResult
  func start(input: Input) -> Output
}

func batchProcess(processor: Processor, input: [Input]) {
  input.forEach { value in
    processor.start(input: value)
  }
}

A: For every type you want to use for the input and output, they have to conform to Input and Output protocols, including String, URL, etc, which ends up with boilerplate code. Another downside is that you’re introducing a new protocol for each parameter and return type. Lastly, adding a new method on Input or Output would require the implementation on all the types that conform to the protocol.

One solution is to use associatedtype: an associated type gives a placeholder name to a type that’s used as part of the protocol. The actual type to use for that associated type isn’t specified until the protocol is adopted. One way to think of an associated type is that it’s a generic that lives inside a protocol. Conforming types can use the typealias keyword to specify the type that will replace each associated type placeholder. In many cases, however, Swift is able to infer that type from the context.

protocol Processor {
  associatedtype Input
  associatedtype Output

  @discardableResult
  func start(input: Input) -> Output
}

func batchProcess<P: Processor>(processor: P, input: [P.Input]) {
  input.forEach { value in
    processor.start(input: value)
  }
}

Like generics, associated types get resolved at compile time.

Q: What is a Self requirement in a protocol?

A: A Self requirement is a placeholder in a protocol for a type, used as part of the protocol, which is replaced by the concrete type that conforms to the protocol. A Self requirement can thus be thought of as a special case of an associated type. Like a protocol with an associated type, a protocol with a Self requirement is also an incomplete protocol since the type to be used in place of the Self placeholder gets resolved only when the protocol is adopted. The difference is that, while a conforming type can use any type it wants to replace an associated type, any Self placeholder must be replaced by the confirming type itself.

Q: In the following code, how could you limit the Input to be only of type String?

func batchProcess<P: Processor>(processor: P, input: [P.Input]) {
  input.forEach { value in
    processor.start(input: value)
  }
}

A:

func batchProcess<P>(processor: P, input: [P.Input]) where P: Processor, P.Input == String {
  input.forEach { value in
    processor.start(input: value)
  }
}

Q: Given the following code, how could you apply protocol inheritance to further constrain it, say Input has to be an URL and Output has to be Data?

protocol Processor {
  associatedtype Input
  associatedtype Output

  @discardableResult
  func start(input: Input) -> Output
}

func batchProcess<P>(processor: P, input: [P.Input]) where P: Processor, P.Input == URL, P.Output == Data {}

A:

protocol URLProcessor: Processor where Input == URL, Output == Data {}

func batchProcess<P: URLProcessor>(processor: P, input: [P.Input]) {}

Q: Given the following piece of code of a Broadcastor with a default implementation, how could you add a default implementation for it that also sanitise the message before broadcasting, using protocol inheritance? And how could you do the same with protocol composition? What are the downsides of each approach?

protocol Broadcaster {
  func send(_ message: String)
}

extension Broadcaster {
  func send(_ message: String) {
    print("Message is sent!")
  }
}

A:

Protocol inheritance:

protocol SanitisingBroadcaster: Broadcaster {
  func sanitise(_ message: String) throws
}

extension SanitisingBroadcaster {
  func send(_ message: String) {
    guard try? sanitise(message) else {
      preconditionFailure("Message invalid!")
    }
    print("Sending: \(message)")
  }
  func sanitise(_ message: String) throws {
    // check message
  }
}

struct OnlineBroadcaster: SanitisingBroadcaster {
  // …
}

let broadcaster = OnlineBroadcaster()
broadcaster.send("Hello, world!")

Protocol composition:

protocol BroadcastSanitiser {
  func sanitise(_ message: String) throws
}

extension BroadcastSanitiser {
  func sanitise(_ message: String) throws {
    // …
  }
}

struct OnlineBroadcaster: Broadcaster, BroadcastSanitiser {}

extension BroadcastSanitiser where Self: Broadcaster {
  func send(_ message) {
    guard try? sanitise(message) else {
      preconditionFailure("Invalid message!")
    }
    print("Sending: \(message)")
  }
}

let broadcaster = OnlineBroadcaster()
broadcaster.send("Hello, world!")

Q: What are conditional conformance?

A: Conditional comformance is conforming to a protocol only if certain conditions are true. For example, making Array conforming to PlayerList only if the array’s elements conform to Player.

protocol Player {
  var historyScore: Int { get }
}

extension Array where Element: Player {
  func totalScore() -> Int {
    return reduce(0) { result, element in
      result + element.historyScore
    }
  }
}

Q: What’s the problem with the following code and how could you improve it?

protocol AdsProtocol {
  func showAds()
}

extension UIViewController: AdsProtocol {
  func showAds() {}
}

A:

extension AdsProtocol where Self: UIViewController {
  func showAds() {}
}

Now a view controller can choose to conform to AdsProtocol and gets the method implementation for free on an as-needed basis.

Back to ToC

Collections

Q: Explain Sequence in Swift

A: A type that provides sequential, iterated access to its elements. A sequence is a list of values that can be stepped through one at a time, the most common way to iterate over the elements of a sequence is to use for-in loop. This capability gives you access to a large number of operations that can be performed on any sequence, such like contains(_:).

To add Sequence conformance to your own custom type, add a makeIterator() method and returns an iterator. Alternatively, if your type can act as its own iterator, implementing the requirements of IteratorProtocol and declaring conformance to both Sequence and IteratorProtocol.

struct Countdown: Sequence, IteratorProtocol {
    var count: Int

    mutating func next() -> Int? {
        guard count > 0 else {
            return nil
        }
        deter { count -= 1 }
        return count
    }
}

Q: Explain zip(_:_:)

A:

zip creates a sequence of pairs built out of two underlying sequences.

let words = ["one", "two", "three", "four"]
let numbers = 1...4

for (word, number) in zip(words, numbers) {
    print("\(word): \(number)")
}

// prints "one: 1"
// prints "two: 2"
// prints "three: 3"
// prints "four: 4"

If the two sequences are different lengths, the resulting sequence is the same length as the shorter sequence:

let naturalNumbers = 1...Int.max
let zipped = Array(zip(words, naturalNumbers))
// zipped == [("one", 1), ("two", 2), ("three", 3), ("four", 4)]

Q: What is Hashable?

A: A type that can be hashed to produce an integer hash value.

The Hashable protocol inherits from Equatable protocol, which must also be satisfied.

Q: What is subscripts?

A: Subscripts are shortcuts for accessing the member elements of a collection, list or sequence, or a custom type that supports subscripts.

Q: What is the difference between reduce and reduce into?

A:

The both can be used to produce a single value from the elements of an entire sequence.

reduce

reduce iterates over a collection, taking an initial value and a closure which applies a transformation to that value. The closure provides two parameters, the partial result obtained in each iteration, and the next element of the collection.

reduce makes sense when you are not creating expensive copies for each iteration, such as reducing into an integer.

reduce into

reduce into passes the partial result by reference instead of value, it is preferred over reduce(_:_:) for efficiency when the result is a copy-on-write type, for example an Array or a Dictionary.

Q: What is the Sequence protocol and how could you implement it?

A: A type that provides sequential, iterated access to its elements. To add Sequence conformance to your own custom type, add a makeIterator() method that returns an iterator. Alternatively, if your type can act as its own iterator, implementing the requirements of the IteratorProtocol protocol and declaring conformance to both Sequence and IteratorProtocol are sufficient.

Q: What is AnyIterator?

A: AnyIterator is a type erased iterator, it accepts a closure when initialized, which is called whenever next is called on the iterator.

Q: [coding] Create a Bin that represents classified garbage bin that stores waste by types with number of count. e.g.

var bin = Bin<String>()
bin.insert("thought")
bin.insert("thought")
bin.insert("thought")
bin.insert("value")
bin.remove("thought")
bin.count // 3
print(bin)
// Output:
// thought occurs 2 times
// value occurs 1 time

let anotherBin: Bin = [1, 2, 2, 5, 5, 5]
print(anotherBin)
// Output:
// 1 occurs 1 time
// 2 occurs 2 times
// 5 occurs 3 times

A:

struct Bin<Element: Hashable>: IteratorProtocol {

  private var store = [Element: Int]()

  mutating func insert(_ element: Element) {
    store[element, default: 0] += 1
  }

  mutating func remove(_ element: Element) {
    store[element]? -= 1
    if store[element] == 0 {
      store[element] = nil
    }
  }

  var count: Int {
    store.values.reduce(0, +)
  }
}

extension Bin: CustomStringConvertible {
  var description: String {
    var summary = String()
    for (key, value) in store {
      let times = value == 1 ? "time" : "times"
      summary.append("\(key) occurs \(value) \(times)\n")
    }
    return summary
  }
}

To implement Sequence you need an iterator:

struct BinIterator<Element: Hashable>: Sequence, IteratorProtocol {
  
  var store = [Element: Int]()

  mutating func next() -> Element? {
    guard let (key, value) = store.first else {
      return nil
    }
    if value > 1 {
      store[key]? -= 1
    } else {
      store[key] = nil
    }
    return key
  }
}

Alternatively, we can implement Sequence by returning a new AnyIterator:

extension Bin: Sequence {
  func make Iterator() -> AnyIterator<Element> {
    var exhaustiveStore = store // create a copy
    return AnyIterator<Element> {
      guard let (key, value) = exhaustiveStore.first else {
        return nil
      }
      if value > 1 {
        exhaustiveStore[key]? -= 1
      } else {
        exhaustiveStore[key] = nil
      }
      return key
    }
  }
}

Finally, we can make Bin implement ExpressibleByArrayLiteral:

extension Bin: ExpressibleByArrayLiteral {
  typealias ArrayLiteralElement = Element
  init(arrayLiteral elements: Element...) {
    store = elements.reduce(into: [Element: Int]()) { (updatingStore, element) in
      updatingStore[element, default: 0] += 1
    }
  }
}

Q: What is IteratorProtocol?

A:

It has an associated type element and that element is the type of the thing that you’re going to be pending or the type of the thing that you’re going to be iterating over, and it has one function called next, which returns the next element and mutates that Iterator:

protocol IteratorProtcol {
    associatedtype Element
    mutating func next() -> Element?
}

Q: What are the differences between Sequence and Collection?

A:

Sequence:

Sequence is a list of elements. It has two important caveats:

  1. it can be either finite or infinite
  2. you can only ever iterate through it one time, sometimes you will be able to iterate it more than once, but your not guaranteed to be able to iterate it more than once

Collection:

Collection inherits from Sequence. Every Collection will always be finite, and you can iterate that Collection as many times as you want.

protocol Collection {
  associatedtype Index: Comparable
  var startIndex: Index
  var endIndex: Index
  subscript(position: Index) -> Iterator.Element { get }
  func index(after index: Index) -> Index
}

Q: [coding] Create a TodoList which is a Collection, with every Day mapping to one or more Activity. Given the definition of Day and Activity below:

struct Activity: Equatable {
  let date: Date
  let description: String
}

struct Day: Hashable {
  let date: Date
  init(date: Date) {
    let unitFlags: Set<Calendar.Component> = [.day, .month, .year]
    let components = Calendar.current.dateComponents(unitFlags, from: date)
    guard let convertedDate = Calendar.current.date(from: components) else {
      self.date = date
      return
    }
    self.date = convertedDate
  }
}

A:

struct TodoList {
  typealias DataType = [Day: [Activity]]
  private var todos = DataType()
  init(activities: [Activities]) {
    self.todos = Dictionary(grouping: activities) { activity in
      Day(date: activity.date)
    }
  }
}

extension TodoList: Collection {
  typealias KeysIndex = DataType.Index
  typealias DataElement = DataType.Element
  var startIndex: KeysIndex { return todos.keys.startIndex }
  var endIndex: KeysIndex { return todos.keys.endIndex }

  func index(after i: KeysIndex) -> KeysIndex {
    return todos.index(after: i)
  }

  subscript(index: KeysIndex) -> DataElement {
    return todos[index]
  }
}

extension TodoList {
  subscript(date: Date) -> [Activity] {
    return todos[Day(date: date)] ?? []
  }
  subscript(day: Day) -> [Activity] {
    return todos[day] ?? []
  }
}

extension TodoList: ExpressibleByArrayLiteral {
  init(arrayLiteral elements: Activity...) {
    self.init(activities: elements)
  }
}

Q: What is the difference between Array and Set in term of performance?

A: Set is faster than Array in general because elements in a Set need to conform to the Hashable protocol which allows Set to be optimized for performance, it takes same amount of time to lookup an element in a small collection vs a large collection.

Back to ToC

UIKit

Q: When multiple people working on different screens defined inside a single storyboard, what is the technique to avoid merge conflict?

A: Use storyboard reference.

Q: What is unwind segue?

A: An unwide segue (sometimes called exit segue) can be used to navigate back through push, modal or popover segues, you can go back multiple steps in navigation hierarchy.

Q: What is the difference between the frame and the bounds?

A: The bounds of an UIView is the rectangle, expressed as location and size relative to its own coordinate system; The frame of an UIView is the rectangle, expressed as location and size relative to the superview it is contained within.

For UIScrollView, its frame can be (0, 0) while its bounds can be non-zero, if its contentOffset isn’t 0.

Q: How could you setup live rendering in storyboard?

A: Using @IBInspectable and @IBDesignable lets you build interface with custom controls and have them rendered in real-time during designing in storyboard.

@IBInspectable properties provide access to user-defined runtime attributes. Accessible from the identity inspector, it’s basically a mechanism for configuring any key-value coded property of an instance in a NIB, XIB, or storyboard.

Built-in view types can also be extended to have inspectable properties beyond the ones already in Interface Builder’s attribute inspector. e.g. the following code exposes the cornerRadius property of a view’s layer:

extension UIView {
    @IBInspectable var cornerRadius: CGFloat {
        get {
            return layer.cornerRadius
        }
        set {
            layer.cornerRadius = newValue
            layer.masksToBounds = newValue > 0
        }
    }
}

Prefixing @IBDesignable (or IB_DESIGNABLE macro in Objective-C) allows Interface Builder to perform live updates on a particular view.

Q: How could you design UI layout so it adapts to different devices, ie. iPhone and iPad?

A: On top of auto-layout, you can use size classes to custom your user interface for given device class, based on its orientation and screen size. Size classes let you add extra layout configuration to your app so that your UI works well across different devices.

Q: What is Intrinsic Content Size?

A: Intrinsic Content Size is something that a view can provide, to indicate the size needed for the view to render properly; It gives information to the Auto Layout engine that a particular view has a predefined size that the engine can use to calculate and lay it out among other views.

Q: What is the layer object of a view?

A: It’s a data object that represents visual content. Layer objects are used by views to render their content. Custom layer objects can also be added to the interface to implement complex animations and other types of sophisticated visual effects.

Q: What is the “file owner” in interface builder?

A: The file owner is the object that loads the nib, i.e. the object which receives the message loadNibNamed: or initWithNibName:; If you want to access any objects in the nib after loading it, you can set an outlet in the file owner. By default View Controllers act as file owners, it’s external to the nib and not part of it, it’s only available when the nib is loaded.

Q: What is Safe area?

A: The safe area of a view reflects the area not covered by navigation bars, tab bars, toolbars and other system component that overlaps a view controller’s view. Additional insets can be specified via additionalSafeAreaInsets.

Q: Difference between accessibilityIdentifier, accessibilityLabel and accessibilityValue

A:

Q: How could you dictate the order in which views are read when VoiceOver is turned on?

A: You can use the method in your container view called index of accessibility element, which returns the index of the specified accessibility element, so you can arrange the order of the subviews in term of VoiceOver.

Q: What’s the difference between xib and storyboard?

A: Both for creating views using interface builder, xib is for view or single view controller, storyboard support multiple view controllers with transition flow.

Q: What is Dynamic Type?

A: Dynamic Type is a feature on iOS that enables the app’s content to scale based on the user’s preferred content size. It helps users who need larger text for better readability. And it also accommodates those who can read smaller text, allowing for more information to appear on the screen.

Q: How could you add shadow to a view?

A: In UIKit, all view layers have options for shadow opacity, radius, offset, color and path. In SwiftUI, you can use the shadow() modifier. Dynamic shadow can be expensive with transparency calculations, rasterising the layer makes the shadow part of the overall bitmap which is less expensive but the current opacity of the layer is not rasterized.

Q: What are your experience using CoreGraphics?

A: Draw shapes and convert UIView into UIImage for saving locally.

Q: What are the benefits of using child view controllers?

A: Dividing a large view controller into child view controllers, the same functionality remains but in several smaller parts which are easier to maintain; Also, there can be cases where a view is reused as subviews in several screens (or view controllers), and the view has a number of business logic in it, which deserves a dedicated view controller.

Q: What are the pros and cons of using viewWithTag()?

A: The only possible pro is that it provides a way to find a subview, but it should not be used just because you can, as using magic number isn’t a good idea.

Q: When would you choose to use a collection view rather than a table view?

A: Collection views can display tiles in columns and rows, it can also handle custom layouts, whereas table view is for linear lists with headers and footers. In iOS 14, UICollectionView got a new feature that lets you include lists in the collection view.

Q: What happens when Color or UIColor has values outside 0 to 1?

A: In RGB color space, values outside of 0 to 1 should be clamped, but with the addition of wide colors, e.g. extended color space, it’s valid to have color values that are outside 0 to 1.

Back to ToC

Concurrency

Q: Why do we need to specify self to refer to a stored property or a method in asynchronous code?

A: Since the code is dispatched to a background thread, we need to capture a reference to the correct object.

Q: What is DispatchGroup?

A: DispatchGroup allows for aggregate synchronization of work, you can use them to submit multiple different work items and track when they all complete.

Q: Swift 5.5 introduced modern concurrency features, what are async, await and Task?

A:

Swift splits up the code into logical units called partial tasks, or partials, the runtime schedules each of them separately for asynchronous execution.

Q: What are the syntax of async/await for function, property and closure?

A:

Function: add async before return type, if the function throws, add async before throws:

func someFunction() async throws -> Int {}
let someValue = try await someFunction()

Computed property: add async to the getter and access it by prepending await:

var someProperty: Int {
  get async {
    
  }
}

let value = await someProperty

Closure: add async to the signature:

func doSomething(completion: (Result<Data, Error>) -> Void) {
  // …
}

doSomething { result in
  await parse(result)
}

Q: What does the runtime do when execution reaches a method called with await?

A:

Q: The following code makes two asynchronous calls, marking them with await makes the second call won’t happen until the first call finishes. How could you make them happen at the same time?

playerProfiles = try await game.playerProfiles()
playerPhotos = try await game.playerPhotos()

A: Swift offers a special syntax that groups several async calls and await them all together:

do {
  async let playerProfiles = try game.playerProfiles()
  async let playerPhotos = try game.playerPhotos()
} catch {
  // …
}

let (profiles, photos) = try await (playerProfiles, playerPhotos)

Q: What is the syntax in modern Swift concurrency world (ie. async/await) that runs code on the main thread, equivalent to DispatchQueue.main.async?

A:

await MainActor.run {}

To make sure a method is executed on the main actor automatically:

@MainActor func someMethod()

Q: What is AsyncSequence?

A: AsyncSequence is a protocol describing a sequence that can produce elements asynchronously, similar to Sequence, except that the next element need to be await.

for try await item in asyncSequence {}
while let item = try await asyncSequence.makeAsyncIterator().next() {}

Q: Is NSNotification send asynchronously or synchronously? If a notification is sent from a non-main thread, in which thread it is received?

A: Synchronous. It will be received on the same non-main thread.

Back to ToC

Language Specific

Q: Describe how to define a custom operator.

A:

  1. Declare:
precedencegroup ExponentPrecedence {
  higherThan: MultiplicationPrecedence
  associativity: right
}
infix operator **: ExponentPrecedence
  1. Implementation:
func **(base: Int, exponent: Int) -> Int {
  let l = Double(base)
  let r = Double(exponent)
  let p = pow(l, r)
  return Int(p)
}

Q: What is ternary operator?

A: Ternary operator operates on three targets, if the first expression is true, it evaluates and returns the value of second expression, otherwise it evaluates and returns the value of the third expression.

Q: How do you define a type that can be initialized with an Int literal? Say

level1 = Level(1)
level2: Level = 2

A:

Swift offers several protocols that allow you to initialize a type with literal values by using the assignment operator.

public struct Level {
  private let level: Int
  public init(_ level: Int) {
    self.level = level
  }
}

extension Level: ExpressibleByIntegerLiteral {
    public init(integerLiteral value: IntegerLiteralType) {
        self.init(value)
    }
}

a: Level = 1

Q: What is the difference between class and static properties or functions?

A:

Q: How does constants in ObjC const differ from a let constant in Swift?

A: A const variable is initialized at compile time with a value or expression that have to be resolved at compile time; A let variable is a constant determined at runtime, it can be initialized with either a static or dynamic expression.

Q: What is LLVM, LLDB and Clang?

A: The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Clang is a C language front-end for LLVM. LLDB builds on libraries provided by LLVM and Clang to provide a great native debugger.

Q: Explain Semaphore in iOS?

A: DispatchSemaphore provides an efficient implementation of a traditional counting semaphore, which can be used to control access to a resource across multiple execution contexts.

Q: What is availability attributes?

A:

It indicates the code should only be called if running system meet the requirement specified.

@available(iOS 9.0, *)

@available(iOS, introduced: 9.0)
@available(OSX, introduced: 10.11)
// is replaced by
@available(iOS 9.0, OSX 10.11, *)

if #available(iOS 9.0, *) {}

Q: What is the difference between Any and AnyObject?

A:

Q: What is property wrapper?

A: A property wrapper adds a layer of separation between code that manages how a property is stored and the code that defines a property. For example, if you have properties that provide thread-safety checks or store their underlying data in a database, you have to write that code on every property. When you use a property wrapper, you write the management code once when you define the wrapper, and then reuse that management code by applying it to multiple properties.

To define a property wrapper, you make a structure, enumeration, or class that defines a wrappedValue property. In the code below, the TwelveOrLess structure ensures that the value it wraps always contains a number less than or equal to 12. If you ask it to store a larger number, it stores 12 instead.

@propertyWrapper
struct TwelveOrLess {
    private var number: Int
    init() { self.number = 0 }
    var wrappedValue: Int {
        get { return number }
        set { number = min(newValue, 12) }
    }
}

Q: What is downcasting?

A:

When we’re casting an object to another type in Objective-C, it’s pretty simple since there’s only one way to do it. In Swift, though, there are two ways to cast — one that’s safe and one that’s not . as used for upcasting and type casting to bridged type as? used for safe casting, return nil if failed as! used to force casting, crash if failed. should only be used when we know the downcast will succeed.

Q: What is inout?

A: inout parameter means if function modifies the parameter as local variable, the passed-in parameter is also modified, it’s like inout means reference type whereas value type if without inout.

Q: Differences between internal, fileprivate, private, and public private(set)?

A:

Q: What are Non-Escaping and Escaping Closures?

A: The lifecycle of a non-escaping closure is simple: Pass a closure into a function The function runs the closure (or not) The function returns Escaping closure means, inside the function, you can still run the closure (or not); the extra bit of the closure is stored some place that will outlive the function. There are several ways to have a closure escape its containing function: Asynchronous execution: If you execute the closure asynchronously on a dispatch queue, the queue will hold onto the closure for you. You have no idea when the closure will be executed and there’s no guarantee it will complete before the function returns. Storage: Storing the closure to a global variable, property, or any other bit of storage that lives on past the function call means the closure has also escaped.

Q: What are designated and convenience initializers?

A: Designated initializers are:

Convenience initializers are:

Every class must have a designated initializer, if this class inherits from another, the designated initializer is responsible for calling the designated initializer of its immediate superclass.

Classes can have any number of convenience initializers, a convenience initializer must call another initializer from the same class, whether it is a designated initializer or another conveniences initializer.

Convenience initializers must ultimately call a designated initializer.

Q: What does final do in Swift?

A: Prevents class or method from being overridden

Q: Differences between open, public?

A:

Q: When a class has more properties than its super class, how could you still use the designated initializer of superclass?

A: override the designated initializer of the superclass, initialize all properties that the subclass defines before calling super’s designated initializer.

Q: What are the purposes of required initializer?

A: Adding required keyword to an initializer assures that subclasses implement the required initializer. The two common reasons are factory methods and protocols.

Q: What is defer?

A: defer keyword provides a safe and easy way to declare a block that will be executed only when execution leaves the current scope.

Defer is usually used to cleanup the code after execution. This might involve deallocating container, releasing memory or close the file or network handle. When put into the routine, code inside defer block is last to execute before routine exits. Routine in this case could refer to a function. Sometimes when function returns there are hanging threads, incomplete operation which needs to cleanup. Instead of having them scattered across function, we can group them together and put in the defer block. When function exits, cleanup code in the defer gets executed.

Q: What is platform limitation of tvOS?

A:

  1. no browser support, this means the app can’t link out to a web browser for things like OAuth or social media sites
  2. cannot explicitly use local storage
  3. app bundle cannot exceeed 4GB

Q: What is ABI?

A:

ABI stands for Application Binary Interface, at runtime, Swift program binaries interact with other libraries through an ABI, it defines many low level details for binary. ABI stability means locking down the ABI to the point that future compiler versions can produce binaries conforming to the stable ABI. It enables binary compatibility between applications and libraries compiled with different Swift versions.

Q: What is KVO?

A: KVO stands for Key-Value Observing and allows a controller or class to observe changes to a property value. In KVO, an object can ask to be notified of any changes to a specific property; either its own or that of another object.

Q: What is KVC?

A: KVC adds stands for Key-Value Coding. It’s a mechanism by which an object’s properties can be accessed using string’s at runtime rather than having to statically know the property names at development time.

Q: What is the difference between Delegate and KVO?

A: Both are ways to have relationships between objects. Delegation is a one-to-one relationship where one object implements a delegate protocol and another uses it and sends messages to it, assuming that those methods are implemented since the receiver promised to comply to the protocol. KVO is a many-to-many relationship where one object could broadcast a message and one or multiple other objects can listen to it and react. KVO does not rely on protocols. KVO is the first step and the fundamental block of reactive programming (RxSwift, ReactiveCocoa, etc.)

Q: What are failable and throwing initializers?

A: Very often initialization depends on external data, this data can exist as it can not, for that Swift provides two ways to deal with this. Failable initializers return nil of there is no data, and let the developer “create” a different path in the application based on that. In other hand throwing initializers returns an error on initialization instead of returning nil.

Q: Can you describe what is conditional conformance and give an example?

A: conditional conformance is a Swift feature introduced in version 4.1, it allows you to automatically conform to certain protocols if all properties in your custom type conform to these protocols.

Q: How could you make your custom type conform to Hashable?

A:

  1. implement the func hash(into hasher: inout Hasher)
  1. also conform to Equatable

Q: What is Codable?

A: Introduced in Swift 4, the Codable API enables us to leverage the compiler in order to generate much of the code needed to encode and decode data to/from a serialized format, like JSON.

Codable is actually a type alias that combines two protocols — Encodable and Decodable. By conforming to either of those protocols when declaring a type, the compiler will attempt to automatically synthesize the code required to encode or decode an instance of that type, which will work as long as we’re only using stored properties that themselves are encodable/decodable.

struct Player: Codable {
    var name: String
    var age: Int
}

Encode a Player into Data:

do {
    let player = Player(name: "Doe", age: 19)
    let encoder = JSONEncoder()
    let data = try encoder.encode(player)
} catch {
    print("Failed to encode player: \(error)")
}

Decode a Player from `Data:

let decoder = JSONDecoder()
let player = try decoder.decode(Player.self, from: data)

Q: How does Codable work if the field names in data source e.g. JSON do not match the name in data model defined in code?

{
    "player_data": {
        "full_name": "John Doe",
        "user_age": 19
    }
}

A: Two ways that have downsides:

  1. Change the code of data model to match with data source. This may introduce unconventional coding conventions like full_name instead of name or fullName; Also, if the project has code quality automation set up, e.g. SwiftLint, it may stop the code from compiling.
  2. Manually perform encoding and decoding. This would require extra code.

Another way is to define a new type that’s specifically used for encoding and decoding:

extension Player {
    struct CodingData: Codable {
        struct Container: Codable {
            var fullName: String
            var playerAge: Int
        }

        var playerData: Container
    }
}

extension Player.CodingData {
    var player: Player {
        return Player(
            name: playerData.fullName,
            age: playerData.userAge
        )
    }
}

To decode:

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let codingData = try decoder.decode(Player.CodingData.self, from: data)
let player = codingData.player

Q: What is the difference between the Float, Double, and CGFloat data types?

A: Float is always 32-bit, Double is always 64-bit, and CGFloat is either 32-bit or 64-bit depending on the device it runs on, but realistically it’s just 64-bit all the time. In Swift 5.5, the compiler can automatically perform conversion between Double and CGFloat values.

Q: What are one-sided ranges and when would you use them?

A: In Swift, you can create a range using closed range operator or the half-open range operator:

let closedRange = 0...9
closedRange.count   // 10

let halfOpenRange = 0..<10
halfOpenRange.count // 10

One-sided ranges allow us to skip either the start or end of a range to have Swift infer the starting point for us:

let items = [
  "keyboard",
  "cable",
  "monitor",
  "harddrive",
  "charger"
]

items[...2] // ["keyboard", "cable", "monitor"]
items[..<2] // ["keyboard", "cable"]
items[2...] // ["monitor", "harddrive", "charger"]

One-sided ranges can be useful when you want to read from a certain portion of a collection, such as if you want to skip the first N elements in an array.

Q: What does it mean when we say “strings are collections in Swift”?

A: This statement means that Swift’s String type conform to the Collection protocol, which means String values have all the methods defined in Collection protocol, for example, you can loop over characters, count how long the string is, map the characters and so on.

Q: What is UUID and how do you generate it?

A: UUID stands for universally unique identifier, which means if you generate a UUID right now using UUID it’s guaranteed to be unique across all devices in the world. It’s a great way to generate a unique identifier for something you need to reference.

let uuid = UUID().uuidString

Q: What is @autoclosure and when would you use it?

A: @autoclosure lets you define an argument that automatically gets wrapped in a closure. It’s usually used when you want to defer the execution of a potentially expensive or unnecessary expression when it’s passed as argument. One example is the assert function which are triggered only in debug builds, there’s no need to evaluate it in release builds.

Q: What is the difference between self and Self?

A: self refers to the current object the code is runing inside, Self refers to the current type the code is running inside.

Q: What does targetEnvironment do?

A: It’s a condition that lets you differentiate between builds that are for physical devices and those that are for a simulated environment.

Q: What are key paths?

A: A key path refers to a property in a type rather than the exact value of that property in one particular instance.

struct Player {
  let name: String
  let age: Int
}

let players: [Players] = // …
let allNames = players.map(\.name)

Q: When would you use defer?

A: When you need to delay a piece of code until a function ends. For example, removing a temporary file when the function finishes.

Q: What is a variadic function?

A: Variadic function accepts any number of parameters, written as ... (three dots):

func arithmeticMean(_ numbers: Double...) -> Double {
    var total: Double = 0
    for number in numbers {
        total += number
    }
    return total / Double(numbers.count)
}

Q: What does the #available syntax do?

A: This syntax was introduced in Swift 2.0 to allow run-time version checking of features by OS version number. It allows you to target an older version of iOS while selectively limiting features available only in newer iOS versions.

Q: What does CaseIteratable do?

A: A type that provides a collection of all of its values.

enum Direction: CaseIterable {
    case north, south, east, west
}

let directions = Direction.allCases

Back to ToC

Objective-C

Q: What is the difference between _ vs self. in Objective-C?

A: You typically use either when accessing a property in Objective-C. When you use _, you’re referencing the actual instance variable directly. You should avoid this. Instead, you should use self. to ensure that any getter/setter actions are honored.

In the case that you would write your own setter method, using _ would not call that setter method. Using self. on the property, however, would call the setter method you implemented.

Q: What are blocks in Objective-C?

A: Blocks are a language-level feature of Objective (C and C++ too). They are objects that allow you to create distinct segments of code that can be passed around to methods or functions as if they were values. This means that a block is capable of being added to collections such as NSArray or NSDictionary. Blocks are also able to take arguments and return values similar to methods and functions.

The syntax to define a block literal uses the caret symbol(^).

Q: What is Method Swizzling?

A: Method swizzling allows the implementation of an existing selector to be switched at runtime for a different implementation in a classes dispatch table. Swizzling allows you to write code that can be executed before and/or after the original method. For example perhaps to track the time method execution took, or to insert log statements.

#import "UIViewController+Log.h"
@implementation UIViewController (Log)
    + (void)load {
        static dispatch_once_t once_token;
        dispatch_once(&once_token,  ^{
            SEL viewWillAppearSelector = @selector(viewDidAppear:);
            SEL viewWillAppearLoggerSelector = @selector(log_viewDidAppear:);
            Method originalMethod = class_getInstanceMethod(self, viewWillAppearSelector);
            Method extendedMethod = class_getInstanceMethod(self, viewWillAppearLoggerSelector);
            method_exchangeImplementations(originalMethod, extendedMethod);
        });
    }
    - (void) log_viewDidAppear:(BOOL)animated {
        [self log_viewDidAppear:animated];
        NSLog(@"viewDidAppear executed for %@", [self class]);
    }
@end

Q: What is the difference between Array and NSArray?

A: Array is a struct, thus it’s value type in Swift, NSArray is an immutable ObjectiveC class, it is reference type and bridged to Array<AnyObject> in Swift.

Q: What is the difference between iVar and @property

A: iVar is like the backing variable for declared @property, @synthesized property will have backing iVar created, @property is used with KVO, and can be overwritten with custom getter and setter.

Q: What is selector in Objc?

A: In Objective-C, selector has two meanings. It can be used to refer simply to the name of a method when it’s used in a source-code message to an object. It also, though, refers to the unique identifier that replaces the name when the source code is compiled. Compiled selectors are of type SEL. All methods with the same name have the same selector. You can use a selector to invoke a method on an object—this provides the basis for the implementation of the target-action design pattern in Cocoa.

Q: Explain the difference between atomic and nonatomic synthesized properties?

A:

Q: What is the difference between strong, weak, readonly and copy?

A:

Q: What is @dynamic in ObjC?

A: @dynamic tells the compiler that getter and setter are implemented somewhere else, examples like subclasses of NSManagedObject.

Q: What is @synthesize in ObjC?

A:

Synthesize generates getter and setter methods for property, there are a few special cases:

Q: By calling performSelector:withObject:afterDelay: is the object retained?

A: Yes, the object is retained. It creates a timer that calls a selector on the current threads run loop. It may not be 100% precise time-wise as it attempts to dequeue the message from the run loop and perform the selector.

Q: What happens when you call autorelease on an object?

A: When you send an object a autorelease message, its retain count is decremented by 1 at some stage in the future. The object is added to an autorelease pool on the current thread. The main thread loop creates an autorelease pool at the beginning of the function, and release it at the end. This establishes a pool for the lifetime of the task. However, this also means that any autoreleased objects created during the lifetime of the task are not disposed of until the task completes. This may lead to the taskʼs memory footprint increasing unnecessarily. You can also consider creating pools with a narrower scope or use NSOperationQueue with itʼs own autorelease pool. (Also important – You only release or autorelease objects you own.)

Q: What’s the output of the following code:

@property (nonatomic, strong) NSString *strongString;
@property (nonatomic, weak)   NSString *weakString;
	
strongString =  [NSString stringWithFormat:@"%@",@"string1"];
weakString =  strongString;
strongString = nil;
	
NSLog(@"%@", weakString);

A:

Q: Are atomic properties thread-safe?

A: It’s only safe for its getter and setter, not safe in other cases like if you have an atomic mutable array and you add objects to it.

Q: What is KVO?

A: Key-value observing is a Cocoa programming pattern you use to notify objects about changes to properties of other objects. It’s useful for communicating changes between logically separated parts of your app—such as between models and views. You can only use key-value observing with classes that inherit from NSObject.

Back to ToC

Error Handling

Q: What do assert and precondition do and what are the differences?

A: They both evaluate, and if the result is false, they both crash the app. The difference is assert is only active in debug mode, whereas precondition is also active in release mode. As a general rule, assert is used for checking your own code for internal errors and precondition is for checking if the business rule are satisfied, for example, if the arguments are valid to proceed.

Q: Explain rethrows vs throws

A:

Q: Explain how could you make your custom Error bridging to NSError (with proper errorDomain, errorCode and errorUserInfo)?

A: Make your custom Error implement the CustomNSError protocol.

Q: What’s the difference between try? and try! for calling a throwing function?

A: try? prevents the propogation of errors, calling a throwing function with try? doesn’t care about the reason for failure if it throws; try! asserts that an error won’t occur, like force unwrap, either it works or you get a crash.

Q: Can you name one or more downsides of how Swift handles errors?

A: Functions that are marked as throwing doesn’t reveal which errors can be thrown.

Q: What is Result type in Swift?

A: Result type is an enum with two cases: a success case and a failure case:

public enum Result<Success, Failure: Error> {
  case success(Success)
  case failure(Failure)
}

Q: What does map and mapError do on Result?

A: map transform the result’s success value if present, for example, if you have a Result<Data, NetworkError> type, and a variable of this type, if its value is success, calling map you can turn it into Result<JSON, NetworkError> type, transforming the successful Data value into a JSON string; If the variable has an error value, calling mapError you can map the failure value using the given transformation and return a new result, for example, you can transform network error into business domain error.

Q: What does Result(catching:) do?

A: It creates a new result by evaluating a throwing closure, capturing the returned value as a success, or any thrown error as a failure.

Back to ToC

Networking

Q: What are the main components of a HTTP URL? What purpose do they have?

A:

Every HTTP URL consists of the following components:

scheme://host:port/path?query
var components = URLComponents() 
components.scheme = "https" 
components.host = "www.test.com" 
components.path = "/request/path" 
components.queryItems = [URLQueryItem(name: "q", value: "swift"),
                         URLQueryItem(name: "quantity", value: "100")] 
let url = components.url
// https://www.test.com/request/path?q=swift&quantity=100

Q: What is URLSession?

A: URLSession is responsible for sending and receiving HTTP requests, created via URLSessionConfiguration

Q: Difference between WKWebView and UIWebView?

A:

UIWebview:

WKWebView:

Q: What is OAuth?

A: OAuth is an open standard authorization protocol or framework that describes how unrelated servers and services can safely allow authenticated access to their assets without sharing the initial, related, single logon credential. One simple example is when you go to log onto a website, it offers a few ways to log on using another website’s/service’s logon.

Q: What are the different ways of showing web content to users?

A:

  1. UIWebView and WKWebView (UIWebView is deprecated)
  1. SFSafariViewController provides a visible standard interface for browsing the web
  2. UIApplication has a method that opens an URL.

Back to ToC

Architecture

Q: Explain polymorphism?

A: Polymorphism allows the expression of some sort of contract, with potentially many types implementing the contract in different ways, each according to their own purpose.

Q: What is Dependency Inversion Principle?

A: DI decouples classes from one another by explicitly providing dependencies for them, rather than having them create the dependencies themselves.

Q: Why is design pattern important?

A: Design patterns are reusable solutions to common problems in software design. They are templates designed to help you write code that’s easy to understand and reuse.

Q: What is Singleton pattern?

A:

The singleton pattern ensures that only one instance exists for a given class and that there’s a global access point to that instance. It usually uses lazy loading to create the single instance when it’s needed for the first time.

Q: What is Facade pattern?

A:

The facade pattern provides a single interface to a complex subsystem. Instead of exposing the user to a set of of classes and their APIs, only a simple unified API is exposed.

Q: What is Decorator pattern?

A:

The decorator pattern dynamically adds behaviours and responsibilities to an object without modifying its code. It’s an alternative to subclassing where you modify a class’s behavior by wrapping it with another object.

In ObjC two common implementations are Category and Delegation, in Swift there are Extension and Delegation.

Q: What is Adapter pattern?

A:

An adapter allows classes with incompatible interfaces to work together, it wraps itself around an object and expose a standard interface to interact with that object.

Protocol A makes request, B is a class that makes special request, adapter implements A and wraps a B instance, its request implementation would call B’s special request.

Q: What is Observer pattern?

A:

In observer pattern, one object notifies other objects of any state changes.

Q: What is Memento pattern?

A:

In memento pattern, your stuff is saved somewhere external, later when needed, externalised state can be restored without violating encapsulation.

Q: What is VIPER?

A:

View, Interactor, Presenter, Entity and Router:

Q: What is delegation pattern?

A: The delegation pattern is a powerful pattern used in building iOS applications. The basic idea is that one object will act on another object’s behalf or in coordination with another object. The delegating object typically keeps a reference to the other object (delegate) and sends a message to it at the appropriate time. It is important to note that they have a one to one relationship.

Q: What is factory pattern?

A: Factory Method is used to replace class constructors, to abstract and hide objects initialization so that the type can be determined at runtime, and to hide and contain switch/if statements that determine the type of object to be instantiated.

Q: What is MVC?

A: MVC stands for Model-View-Controller. It is a software architecture pattern for implementing user interfaces.

MVC consists of three layers: the model, the view, and the controller.

Q: What is MVVM pattern?

A:

The MVVM defines the following:

The key aspect of the MVVM pattern is the binding between the View and the View Model. In other words, the View is automatically notified of changes to the View Model.

In iOS, this can be accomplished using Key-Value-Observer (KVO) Pattern. In the KVO Pattern (https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html) , one object is automatically notified of changes to the state of another object. In Objective C, this facility is built into the run-time. However, it’s not as straightforward in Swift. One option would be to add the “dynamic” modifiers to properties that need to be dynamically dispatched. However, this is limiting as objects now need to be of type NSObject. The alternate is to simulate this behavior by defining a generic type that acts as a wrapper around properties that are observable.

Q: What is MVP pattern?

A:

The MVP defines the following:

In iOS, the interaction between the View and Presenter can be implemented using the Delegation Pattern (https://developer.apple.com/library/content/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html). The Delegation Pattern allows one object to delegate tasks to another object. In iOS, the pattern is implemented using a Protocol. The Protocol defines the interface that is to be implemented by the delegate.

In the MVP pattern, the View and Presenter are aware of each other. The Presenter holds a reference to the view it is associated with.

The PresenterProtocol defines the base set of methods that any Presenter must implement. Applications must extend this Protocol to include application specific methods.

protocol PresenterProtocol: class{
    func attachPresentingView(_ view: PresentingViewProtocol)
    func detachPresentingView(_ view: PresentingViewProtocol)
}

The PresentingViewProtocol defines the base set of methods that View must implement. By providing default implementation of the methods in this interface, the conformant view does not have to provide its own implementation. This interface can be extended to define application specific methods.

protocol PresentingViewProtocol: class{
    func dataStartedLoading()
    func dataFinishedLoading()
    func showErrorAlertWithTitle(_ title: String?, message: String)
    func showSuccessAlertWithTitle(_ title: String?, message: String)
}

Q: Can you give some examples of where singletons might be a good idea?

A: When there’s something that can have a single instance exist at any time, and it doesn’t makes sense to have multiple instances of it, for example, UIApplication. Although, direct use of UIApplication can be avoided by having specific protocol for the functionalities in UIApplication and refer to the protocol instead of UIApplication’s shared instance, this way you can have dependency injection and it’s also useful for unit testing.

Back to ToC

App Submission

Q: What is “app ID” and “bundle ID”?

A: Before code signing app during distribution, XCode automatically prefixes the bundle ID with team ID - the unique character sequence issued by Apple to each development team - and stores the combined string as the app ID. Bundle ID unique identifies an application in Apple’s ecosystem.

Q: What is bitcode?

A: Bitcode refers to the type of code: “LLVM Bitcode” that is sent to iTunes Connect, this allows Apple to use certain calculations to re-optimize apps further and this can be done without uploading a new build.

Slicing is the process of Apple optimizing app for user’s specific device App Thinning is the combination of slicing, bitcode and on-demand resources

Q: What is the purpose of code signing in Xcode?

A: It’s useful for verifying developer identity to make sure the app shipped is safe, also it validates the functionalities enabled by the provisioning profile.

Back to ToC