iOS devices makeup over 50% of the Australian mobile operating system market, and capture around the same market share in the US. And Swift as a skill and iOS Developer roles are intrinsically intertwined and according to ZipRecruiter, the average iOS Developer salary in the US in 2020 is $114,614 per year. Follow along and learn 27 advanced Swift interview questions (answered and solved) every experienced iOS Developer shall know before next tech interview.
as? or as! when sure about the type== and ===?== operator checks if the values are the same, comparing value types. "equal to"=== operator checks if the references point the same instance (both point to the same memory address), comparing reference types. "identical to"Consider:
var first = ["John", "Paul"]
let second = ["George", "Ringo"]How to append one array to another array in Swift? Note: the first array mutable so we can join the second array to it.
One option for joining arrays is the append(contentsOf:) method, used like this:
first.append(contentsOf: second)Another option is using the += operator, which is overloaded for arrays to combine elements:
first += secondThe third option is to use a regular + to create a new array by combining two others:
let third = first + secondAll three options produce the same resulting array.
Consider:
let numbers = 1...100
for number1 in numbers {
for number2 in numbers {
if number1 == number2 && number1 * number2 == 144 {
print("Square found: \(number1)")
}
}
}How to exit both loops as soon as the correct number 144 is found?
Consider:
outerLoop: for number1 in numbers {
for number2 in numbers {
if number1 == number2 && number1 * number2 == 144 {
print("Square found: \(number1)")
break outerLoop
}
}
}Notice the outerLoop: before the for number1 loop, and also the matching break outerLoop – that will cause both loops to exit as soon as the correct number is found.
Consider:
let list = [Int](1...5)
var arrayOfTuples = [(Int, Int)]()
for (index, element) in list.enumerated() {
arrayOfTuples += [(index, element)]
}
print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]Can you rewrite this code in a more "swiftier" way?
Yeap, the way to do it is:
let list = [Int](1...5)
let arrayOfTuples = Array(list.enumerated())
print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]or with map:
let list = [Int](1...5)
let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] }
print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]]The features classes and structs have in common:
init()extension (this is important!)Classes support a few more capabilities that structs don’t have:
deinit() function before the class is destroyedmutating keyword mean?Being the value type structs are immutable. Meaning other variables can not change the values for instance of structure at any given point.
The mutating word is required for changing the values of self variables inside structure's function only.
For. e.g
struct MyStruct {
var abc: String = "initila value"
func changeValue() {
abc = "some other value". //Compile time error: Cannot assign to property: 'self' is immutable. Mark method 'mutating' to make 'self' mutable.
}
}Here as we are trying to change value of variable abc inside function declared in struct itself, we get the compile time error.
So here we need to make function mutating to make change value inside structure. Hence the correct code will be:
struct MyStruct {
var abc: String = "initila value"
mutating func changeValue() {
abc = "some other value"
}
}Many functions in iOS accept multiple parameters where the final parameter is a closure. Trailing closure syntax is a little piece of syntactic sugar that makes particularly common code more pleasant to read and write.
Consider:
func greetThenRunClosure(name: String, closure: () -> ()) {
print("Hello, \(name)!")
closure()
}Use:
greetThenRunClosure(name: "Paul") {
print("The closure was run")
}This functionality is available wherever a closure is the final parameter to a function.
inout parameter?Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error.
inout means that modifying the local variable will also modify the passed-in parameters. Without it, the passed-in parameters will remain the same value. Trying to think of reference type when you are using inout and value type without using it.
swap function that it will modify the passed-in parameters.inout is to prefer as associated function parameter.struct MyStruct {
private var myInt: Int = 1
// ... lots and lots of stored properties
mutating func increaseMyInt() {
myInt += 1
}
}
/* call to function _copies_ argument to function property 'myHugeStruct' (copy 1)
function property is mutated
function returns a copy of mutated property to caller (copy 2) */
func myFunc(var myHugeStruct: MyStruct) -> MyStruct {
myHugeStruct.increaseMyInt()
return myHugeStruct
}
/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) {
myHugeStruct.increaseMyInt()
}
var a = MyStruct()
a = myFunc(a) // copy, copy: overhead
myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocationas?, as! and as in Swift?as can only be used for upcasting (or disambiguation) and pattern matching:// 'as' for pattern matching
switch item {
case let obj as MyObject:
// this code will be executed if item is of type MyObject
case let other as SomethingElse:
// this code will be executed if item is of type SomethingElse
...
}as? produces an optional value, which is either the value if it can be cast to the specified type, or nil if it can't.as! doesn't produce an optional, it just produces a value of the specified type, and if the cast fails, it aborts the program. Saying foo as! SomeType is basically the same thing as saying (foo as? SomeType)! (except you get a better error message).You should only ever use as! if you're 100% certain the cast will succeed (because if you're wrong, the whole program aborts).
Self vs self?When you’re writing protocols and protocol extensions, there’s a difference between Self (capital S) and self (lowercase S). When used with a capital S, Self refers to the type that conform to the protocol, e.g. String or Int. When used with a lowercase S, self refers to the value inside that type, e.g. “hello” or 556.
As an example, consider this extension on BinaryInteger:
extension BinaryInteger {
func squared() -> Self {
return self * self
}
}Remember, Self with a capital S refers to whatever type is conforming to the protocol. In the example above, Int conforms to BinaryInteger, so when called on Int the method returns an Int.
On the other hand, self with a lowercase S refers to whatever value the type holds. If the example above were called on an Int storing the value 5 it would effectively be 5 * 5.
In their basic form, a protocol describes what an unknown type of object can do. You might say it has two or three properties of various types, plus methods. But that protocol never includes anything inside the methods, or provides actual storage for the properties.
In a more advanced form, you can write extensions on your protocols that provide default implementations of the methods. You still can’t provide storage for properties, however.
In comparison, classes are concrete things. While they might adopt protocols – i.e., say they implement the required properties and methods – they aren’t required to do that. You can create objects from classes, whereas protocols are just type definitions. Try to think of protocols as being abstract definitions, whereas classes and structs are real things you can create.
strong, weak and unowned references?You should use a set rather than an array if all the following criteria are true:
Hashable types, either your own or one of Swift’s built-in types likes strings and integers. Sets use hash values for fast look up of items.self in a method?By far the most common reason for using self is inside an initializer, where your likely to want parameter names that match the property names of your type, like this:
struct Student {
var name: String
var bestFriend: String
init(name: String, bestFriend: String) {
print("Enrolling \(name) in class…")
self.name = name
self.bestFriend = bestFriend
}
}mutating function?weak and unowned references. Provide an example.fileprivate and private?open and public keywords in Swift?[String: Any] or [String: AnyObject]?Any and AnyObject?Hashable protocol?init?() and init()?Rust has been Stack Overflow’s most loved language for four years in a row and emerged as a compelling language choice for both backend and system developers, offering a unique combination of memory safety, performance, concurrency without Data races...
Clean Architecture provides a clear and modular structure for building software systems, separating business rules from implementation details. It promotes maintainability by allowing for easier updates and changes to specific components without affe...
Azure Service Bus is a crucial component for Azure cloud developers as it provides reliable and scalable messaging capabilities. It enables decoupled communication between different components of a distributed system, promoting flexibility and resili...