iOS Interview Questions Part 3: Swift 🩅

Chetan Aggarwal
18 min readFeb 19, 2018

--

Even though Objective-C is robust and used for many years for developing iOS and macOS apps, Swift is fresh breeze in the air. Do check it out, if you have missed PART: 2 Objective-C. In this part we will discuss some concept of Swift language and iOS programming.

Q: Define Access control ?

Access control restricts access to parts of your code from the code in other source files and modules. This feature enables you to hide the implementation details of your code and to specify a preferred interface through which that code can be accessed and used.

In Swift 3 and swift 4, we have open, public, internal, fileprivate, and private for access control. Open access is the highest (least restrictive) access level and private access is the lowest (most restrictive) access level. Almost all entities in your code have a default access level of internal.

Q: fileprivate VS private VS internal access level ?

  • Internal access enables entities to be used within any source file from their defining module, but not in any source file outside of that module. You typically use internal access when defining an app’s or a framework’s internal structure. Consider framework or module names as ‘Alpha’ with internal class and internal method, if another framework named ‘Beta’ imports ‘Alpha’ then internal class and method of ‘Alpha’ will not be available to framework ‘Beta’.
  • File-private access restricts the use of an entity to its own defining source file. Use file-private access to hide the implementation details of a specific piece of functionality within an entire file. Consider a class ‘Alpha’ with file-private member, class ‘Beta’ with having an object of ‘Alpha’ will not be able to access file-private method since it is file restricted.
  • Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration. Consider class ‘Alpha’ and its extension in the same file with the private and file-private method in both, they can access each other’s method. Class ‘Beta’ defined in the same file cannot access both private methods but can access file-private methods of class ‘Alpha’.

Q: open VS public access control ?

  • public classes and class members can only be subclassed and overridden within the defining module (target).
  • open classes and class members can be subclassed and overridden both within and outside the defining module (target).
  • Consider framework or module ‘Alpha’ with public class, public method, open class and open method, framework ‘Beta’ imports ‘Alpha’, both classes and methods will be available to framework ‘Beta’ but only open class is subclassable and open methods can be override.

Q: Please explain final keyword into the class ?

Preventing Overrides: You can prevent a method, property, or subscript from being overridden by marking it as final. Do this by writing the final modifier before the method, property, or subscript’s introducer keyword (such as final var, final func, final class func, and final subscript).

Any attempt to override a final method, property, or subscript in a subclass or subclass a final class are reported as a compile-time error. Methods, properties, or subscripts that you add to a class in an extension can also be marked as final within the extension’s definition.

Q: Explain [weak self] and [unowned self] ?

unowned does the same as weak with one exception: The variable will not become nil and therefore the variable must not be an optional.

By declaring it [weak self] you get to handle the case that it might be nil inside the closure at some point and therefore the variable must be an optional. A case for using [weak self] in an asynchronous network request, is in a view controller where that request is used to populate the view.

Q: Extension in swift ?

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. (Unlike Objective-C categories, Swift extensions do not have names.)

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

In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions.

Q: Non-Escaping and Escaping Closures

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: A closure is said to escape a function when the closure is passed as an argument to the function, but is called after the function returns. we have to refer to self explicitly within the escaping closure. 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. More Detail: SwiftUnbox and Apple Docs.

Q: map vs flatmap

Use map to loop over a collection and apply the same operation to each element in the collection. The map function returns an array containing the results of applying a mapping or transform function to each item

Use flatMap to iterate and flatten a collection of collections. It is also removes optionals and nil in resultant array. More detail.

Q: filter VS reduce

Use filter to loop over a collection and return an Array containing only those elements that match an include condition.

Use reduce to combine all items in a collection to create a single new value. More detail.

To see all methods available from Sequence, take a look at the Sequence docs.

Q: Define Type alais ?

A type alias declaration introduces a named alias of an existing type into your program. Type alias declarations are declared using the typealias keyword.

typealias MyDict = Dictionary<String, Int>

After a type alias is declared, the aliased name can be used instead of the existing type everywhere in your program. The existing type can be a named type or a compound type. Type aliases do not create new types; they simply allow a name to refer to an existing type.

Apple Docs

Q: Type safety & Type Inference ?

Type Safety: Swift is a type-safe language. A type safe language encourages you to be clear about the types of values your code can work with. If part of your code expects a String, you can’t pass it an Int by mistake.

var welcomeMessage: String
welcomeMessage = 22 // this would create an error because you
//already specified that it's going to be a String

Type Inference: If you don’t specify the type of value you need, Swift uses type inference to work out the appropriate type. Type inference enables a compiler to deduce the type of a particular expression automatically when it compiles your code, simply by examining the values you provide.

var meaningOfLife = 42 // meaningOfLife is inferred to be of type Int
meaningOfLife = 55 // it Works, because 55 is an Int

Apple Docs

Q: Type annotation?

A type annotation explicitly specifies the type of a variable or expression. Type annotations begin with a colon (:) and end with a type.

let someTuple: (Double, Double) = (3.14159, 2.71828)
func someFunction(a: Int) { /* ... */ }

Q: Explain Forced Unwrapping ?

When we define a variable as optional, then to get the value from it, we need to unwrap it. This just means putting an exclamation mark at the end of the variable.

Q: Optional binding ?

Use optional binding to find out whether an optional contains a value, and if so, to make that value available as a temporary constant or variable.

if let constantName = someOptional {
statements
}

Q: Optional Chaining ?

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, then the property, method, or subscript call succeeds; and if the optional is nil, then it returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

Detailed article.

Q: Closures VS blocks

“Swift closures and Objective-C blocks are compatible, so you can pass Swift closures to Objective-C methods that expect blocks. Swift closures and functions have the same type, so you can even pass the name of a Swift function.

Closures have similar capture semantics as blocks but differ in one key way: Variables are mutable rather than copied. In other words, the behavior of __block in Objective-C is the default behavior for variables in Swift.”

Q: Explain Identical operator ?

In swift 3 and above

=== (or !==)

  • Checks if the values are identical (both point to the same memory address).
  • Comparing reference types.
  • Like == in Obj-C (pointer equality).

== (or !=)

  • Checks if the values are the same.
  • Comparing value types.
  • Like isEqual: in Obj-C (However, you can override isEqual in Obj-C).

Q: What is Downcasting ?

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 Nil Coalescing & Ternary Operator ?

The ternary conditional operator is a special operator with three parts, which takes the form question ? answer1 : answer2. It’s a shortcut for evaluating one of two expressions based on whether question is true or false. If question is true, it evaluates answer1 and returns its value; otherwise, it evaluates answer2 and returns its value.

The nil-coalescing operator (a ?? b) unwraps an optional a if it contains a value, or returns a default value b if a is nil. The expression a is always of an optional type. The expression b must match the type that is stored inside a. Apple Doc.

Q: Explain what is defer ?

defer keyword which provides a block of code that will be executed in the case when execution is leaving the current scope. Nice article by NSHipster.

Q: What are benefits of Guard ?

There are two big benefits to guard. One is avoiding the pyramid of doom, avoids lots of annoying if-let statements nested inside each other, moving further and further to the right. The other benefit is provide an early exit out of the function using break, continue or return.

Q: Explain generics in Swift ?

Generics create code that does not get specific about underlying data types. A good detailed article.

Q: What is the Swift main advantage ?

  • Optional Types, which make applications crash-resistant
  • Built-in error handling
  • Closures
  • Much faster compared to other languages
  • Type-safe language
  • Unified Memory Management (Complete ARC)
  • Dynamic library inclusion (reduce size)
  • Supports pattern matching
  • Playground feature

Q: Please explain Swift’s pattern matching techniques?

  • Tuple patterns are used to match values of corresponding tuple types.
  • Type-casting patterns allow you to cast or match types.
  • Wildcard patterns match and ignore any kind and type of value.
  • Optional patterns are used to match optional values.
  • Enumeration case patterns match cases of existing enumeration types.
  • Expression patterns allow you to compare a given value against a given expression.

Q: How is an “inout” parameter different from a regular parameter?

A Inout passes by reference while a regular parameter passes by value.

Q: Define Properties in swift ?

Stored properties store constant and variable values as part of an instance, whereas computed properties calculate (rather than store) a value instead of storing it as a part of an instance. Computed properties are provided by classes, structures, and enumerations. Stored properties are provided only by classes and structures.

Q: Explain Stored Property?

Stored properties can be either variable stored properties (introduced by the varkeyword) or constant stored properties (introduced by the let keyword).

Stored Properties of Constant Structure Instances: If you create an instance of a structure and assign that instance to a constant, you cannot modify the instance’s properties, even if they were declared as variable properties.

NOTE: This behavior is due to structures being value types. When an instance of a value type is marked as a constant, so are all of its properties.

The same is not true for classes, which are reference types. If you assign an instance of a reference type to a constant, you can still change that instance’s variable properties.

Q: Explain Computed Property ?

Classes, structures, and enumerations can define computed properties, which do not actually store a value. Instead, they provide a getter to retrieve and an optional setter to set other properties/values indirectly.

A computed property with a getter but no setter is known as a read-only computed property. It always returns a value, and cannot be set to a different value.

NOTE: You must declare computed properties — including read-only computed properties — as variable properties with the var keyword, because their value is not fixed. The let keyword is only used for constant properties, to indicate that their values cannot be changed once they are set as part of instance initialization.

Q: Explain Lazy stored Property ?

An initial value of the lazy stored properties is calculated only when the property is called for the first time.

Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete. They are also useful when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.

Lazy rules:

  • You can’t use lazy with let .
  • You can’t use it with computed properties . Because, a computed property returns the value every time we try to access it after executing the code inside the computation block.
  • You can use lazy only with members of struct and class .
  • Lazy variables are not initialised atomically and so is not thread safe.

NIce Article on Lazy.

Q: Explain Property observers ?

Property observers observe and respond to changes in a property’s value. They are called every time a property’s value is set, even if the new value is the same as the property’s current value. You can add property observers to any stored properties, except for lazy . We can add property observers to ‘inherited’ property by method ‘overriding’.

You have the option to define either or both of these observers on a property:

  • willSet is called just before the value is stored, it’s passed the new property value as a constant parameter. You can specify a parameter else default name newValue is used.
  • didSet is called immediately after the new value is stored, it’s passed a constant parameter containing the old property value. You can name the parameter or use the default parameter name of oldValue.

Q: Explain Type Property ?

Properties that belong to the type itself, not to any one instance of that type. There will only ever be one copy of these properties, no matter how many instances of that type you create. These kinds of properties are called type properties. You define type properties with the static keyword. For computed type properties for class types, you can use the class keyword instead to allow subclasses to override the superclass’s implementation.

Q: What are Initialization in swift ?

Initialization is the process of preparing an instance of a class, structure, or enumeration for use. This process involves setting an initial value for each stored property on that instance and performing any other setup or initialization that is required before the new instance is ready for use.

Classes and structures must set all of their stored properties to an appropriate initial value by the time an instance of that class or structure is created. Stored properties cannot be left in an indeterminate state.

Initializers, are like special methods that can be called to create a new instance of a particular type.

Apple Docs

Q: Explain Parameter Names and Argument Labels ? How to initialize without Argument Label ?

As with function and method parameters, initialization parameters can have both a parameter name for use within the initializer’s body and an argument label for use when calling the initializer. Swift provides an automatic argument label for every parameter in an initializer if you don’t provide one, since initialiser don’t have a name as compared to functions. Without aurgument label in initializers compile-time throws an error.

Initializer Parameters Without Argument Labels: If you do not want to use an argument label for an initializer parameter, write an underscore (_) instead of an explicit argument label for that parameter to override the default behavior.

Apple Docs

Q: Expain Default Initializer ?

Swift provides a default initializer for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. The default initializer simply creates a new instance with all of its properties set to their default values.

Structure types automatically receive a memberwise initializer if they do not define any of their own custom initializers. Even if it has stored properties that do not have default values. Apple Docs

Q: Explain Initializer Delegation for Value Types ?

Initializers can call other initializers to perform part of an instance’s initialization. This process is known as initializer delegation. It avoids duplicating code across multiple initializers.

Note: Initializer delegation works differently for value types and class types. It is simple for Value types, since they do not support inheritance. Classes have additional responsibilities for ensuring that all stored properties they inherit are assigned a suitable value during initialization.

By defining a custom initializer for a value type, access to the default initializer for that type is restricted. This constraint prevents a situation in which additional essential setup provided in a more complex initializer is accidentally circumvented by someone using one of the automatic initializers.

Apple Docs

Q: Designated initialisers VS Convenience initialisers?

All of a class’s stored properties — including any properties the class inherits from its superclass — must be assigned an initial value during initialization.

Designated initializers are the primary initializers for a class. It initializes all properties of the class and calls an appropriate superclass initializer to continue the initialization process up the superclass chain. Every class must have at least one designated initializer. Designated initializers are “funnel” points through which initialization takes place.

Convenience initializers are secondary supporting initializers for a class. You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer with some of the designated initializer’s parameters set to default values. You can also define a convenience initializer to create an instance of that class for a specific use case or input value type. It is non mandatory. Create convenience initializers whenever a shortcut to a common initialization pattern will save time or make initialization of the class clearer in intent.

Convenience initializers are written in the same style, but with the convenience modifier placed before the init keyword, separated by a space

Apple Docs

Q: Explain Initialiser designation for class types ?

To simplify the relationships between designated and convenience initializers, Swift applies the following three rules for delegation calls between initializers:

Rule 1: A designated initializer must call a designated initializer from its immediate superclass.

Rule 2: A convenience initializer must call another initializer from the same class.

Rule 3: A convenience initializer must ultimately call a designated initializer.

Note: Designated initializers must always delegate up. Convenience initializers must always delegate across. Apple Docs

Q: Explain Two Phase Initialization ?

Class initialization in Swift is a two-phase process. In the first phase, each stored property is assigned an initial value by the class that introduced it. Once the initial state for every stored property has been determined, the second phase begins, and each class is given the opportunity to customize its stored properties further before the new instance is considered ready for use.

Swift’s compiler performs four safety-checks to make sure that two-phase initialization is completed without error:

Safety check 1: A designated initializer must ensure that all of the properties introduced by its class are initialized before it delegates up to a superclass initializer.

Safety check 2: A designated initializer must delegate up to a superclass initializer before assigning a value to an inherited property. If it doesn’t, the new value the designated initializer assigns will be overwritten by the superclass as part of its own initialization.

Safety check 3: A convenience initializer must delegate to another initializer before assigning a value to any property (including properties defined by the same class). If it doesn’t, the new value the convenience initializer assigns will be overwritten by its own class’s designated initializer.

Safety check 4: An initializer cannot call any instance methods, read the values of any instance properties, or refer to self as a value until after the first phase of initialization is complete. Apple Docs

Q: Explain Automatic Initializer Inheritance?

Subclasses do not inherit their superclass initializers by default.However, superclass initializers are automatically inherited if certain conditions are met.

If we provide default values for any new properties you introduce in a subclass, the following two rules apply:

  • Rule 1 : If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.
  • Rule 2: If your subclass provides an implementation of all of its superclass designated initializers — either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition — then it automatically inherits all of the superclass convenience initializers.

Apple Docs

Q: Explain Failable initialisers ?

We cannot always assume that the initialization will always succeed for a struct, enum or class. It can fail for several reasons. It is sometimes useful to define a class, structure, or enumeration for which initialization can fail. You write a failable initializer by placing a question mark after the initkeyword (init?).

Note: 1. You cannot define a failable and a nonfailable initializer with the same parameter types and names.

2. In swift, the initializers won’t return anything. But objective -C does. In swift, You write return nil to trigger an initialization failure, you do not use the return keyword to indicate initialization success.

A failable initializer creates an optional value of the type it initializes. You write return nil within a failable initializer to indicate a point at which initialization failure can be triggered. ie; if a condition fails, you can return nil .

Apple Docs.

Q: Explain Initializing with closures ?

If a stored property’s default value requires some customization or setup, you can use a closure or global function to provide a customized default value for that property. Whenever a new instance of the type that the property belongs to is initialized, the closure or function is called, and its return value is assigned as the property’s default value.

Q: Define Capture list ?

If you want to capture the value of a variable at the point of the closure creation, instead of having it evaluate only when the closure executes, you can use a capture list.

Capture lists are written between square brackets right after the closure’s opening bracket (and before the closure’s arguments / return type if any)

To capture the value of a variable at the point of the closure’s creation (instead of a reference to the variable itself), you can use the [localVar = varToCapture]capture list.

Must Read!

Q: Explain Enum and associated value ?

An enumeration is a user-defined data type which consists of set of related values. Keyword enum is used to defined enumerated data type.

You can define Swift enumerations to store associated values of any given type, and the value types can be different for each case of the enumeration if needed. Enumerations similar to these are known as discriminated unions, tagged unions, or variants in other programming languages.

Souce: TutorialsPoint

Apple Docs

//Enum with Associated Values
enum Barcode {
case upc(Int, Int, Int, Int)}
case qrCode(String)
}

Q: Explain Implicitly Assigned Raw Values ?

When you’re working with enumerations that store integer or string raw values, you don’t have to explicitly assign a raw value for each case. When you don’t, Swift will automatically assign the values for you.

For instance, when integers are used for raw values, the implicit value for each case is one more than the previous case. If the first case doesn’t have a value set, its value is 0.

Q: Value type vs Reference type ?

Value Types:

  • Keep a unique copy of their data.
  • Copies are almost cheap.
  • Runs in constant O(n) time. Since they use fix number of reference count, based on size of the type.
  • While using let, instance of value type and its properties cannot change.
  • Mutability is easy.
  • Highly optimistimized by compiler.
  • Shares field inline.
  • Copy On Write: compiler will make copy of data of Value type when mutation is made. It uses intelligent memory to determine when buffer can be shared.

Reference Type:

  • Share single copy of their data.
  • In Objective-C, Everything is inherit from NSObject, is stored as reference type..
  • faster to assign a refence to a variable.
  • While using let, we can still mutate its instance itself.
  • For Mutability, implement immutable and mutable class variants.
  • Heap is expensive.
  • Takes longer time to initialize.
  • Objects have 2 machine word of overhead: one points to mata class objects and other contains reference count and other booking keep data objects.

Souce: Raywenderlich Article

Where to go next ⏩

Thank you for reading đŸ§‘đŸ»â€đŸ’»

Be sure to clap đŸ‘đŸŒ and follow đŸš¶đŸ»â€â™‚ïž

Questions❓Feedback đŸ“« — please drop you comments 💭

If you like this article, feel free to share it with your friends 📹

Follow me: Linkedin | X(Twitter) | Github đŸ€đŸŒ

--

--