Skip to main content

44 questions tagged with "Swift"

Swift tag description

View All Tags

What is an associated value?

One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

An associated value is the term used to describe the value accompanying acasein a Swift enum. Associated values allow us to present more nuanceddata by adding contextual information to ourcases.

enum Distance {
case km(Int)
case meters(Int)
case miles(value: Int)
}

Distance.miles(value: 20 )

With Swift, names can be specified for associated values in order to make their use more understandable. Additionally, eachcasecan be associatedwith values of any type and number.

enum Action {
case tackle
case random
case kick(power: Int, speed: Float)
case jump(height: Int)
case shootLasers(useBothLasers: Bool)
}

What is an inout parameter?

2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Whenever you pass a value type into a function, only a copy of the value is passed along. As a result, even if you attempt to change the value of that parameter inside the function, the variable at the calling site will still maintain its original value.

var currentAge = 26

func updateAge(passedInAge: Int){
var passedInAge = passedInAge
passedInAge = 42
print(passedInAge) // 42
print(currentAge) // 26
}

updateAge(passedInAge: currentAge)

If we want to change the value of the parameter itself instead of just working with a copy of the data, we鈥檒l need to add the inout keyword. This will allow us to make changes directly to the variable that was passed in even if it鈥檚 a value type.

We鈥檒l need to use the & symbol when providing an inout parameter:

var currentAge = 26

func updateAgeWithInout(passedInAge: inout Int) {
passedInAge = 42
print(passedInAge) // 42
print(currentAge) // 42
}

// currentAge is 26 before the call and 42 after
updateAgeWithInout(passedInAge: &currentAge)

The inout keyword is used very often in Swift and enables syntactic sugar like the += operator which modifies the value of the variable on the left-hand side of the operator in place.

What is optional chaining?

One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Optional chaining is a convenient way of unwrapping multiple Optional properties sequentially. If any of the Optional values in theexpression resolve tonil, the entire expression will resolve tonil.

Consider the following expression involving multipleOptionalproperties:

user?.isAdmin?.isActive

If user or isAdmin or isActive is nil, the entire expression becomes nil. Otherwise, isActive will return the unwrapped value.

This is much more readable than using multiple guard and if let statements to break down a sequence of Optional values.

What is protocol composition? How does it relate to Codable?

One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

The Codable protocol is a prime example of Swift鈥檚protocol composition feature which allows you to easily combine existing protocols together using the & operator.

For example, the Codable protocol is actually thecombination of the Encodable and Decodable protocols.

typealias Codable = Decodable & Encodable

Decodable allows a type to decode itself from an external representation and Encodable allows a type to encode itself as an external representation.

When combined together like this, the Codable protocol ensures that whatever object implements this protocol can both convert and be converted from some externalrepresentation.

In practice, this typically means converting a JSON response to a domain model object and vice-versa.

What is the difference between Any and AnyObject?

One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

When we need to work with non-specific types, we have two options to pick from: Any and AnyObject.

Any is used to refer to any instance of a class, struct, function, enum, etc. This is particularly useful when you鈥檙e working with a variety of data:

let items: [Any] = [ 1 , UIColor.red, "Blue", Toggle()]

AnyObject is more restrictive as it refers to any instance of a class type. You鈥檒l use this when you only want to work with reference types or when you want to restrict a protocol to only be used with a class type:

protocol ClassOnlyProtocol: AnyObject {...}

In contrast, Any can be used with both value and reference types.

It鈥檚 preferable to be as specific as possible about the type you鈥檙e using, so I鈥檇 encourage you to only use Any or AnyObject when you specifically needthe behavior they provide.

What is the difference between == and ===?

2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

In the example below, we have a simpleEngineclassthat implements the Equatable protocol. This allows us to provide a custom implementation for the == operator.

We say that two Engines are equal if the horsepower is the same. So, as long as the Int values match, == will return true.

class Engine: Equatable {
var horsepower: Int

init(horsepower: Int) {
self.horsepower = horsepower
}

static func == (lhs: Engine, rhs: Engine) -> Bool {
lhs.horsepower == rhs.horsepower
}
}

let engine1 == Engine(horsepower: 100 )
let engine2 == Engine(horsepower: 100 )
let engine3 == Engine(horsepower: 200 )

engine1 == engine2 // true
engine2 == engine3 // false

With === we鈥檙e asking if the objects on either sideof the operator point to the same reference. In other words, do they point to the same place in memory?

In the following example, when we compare engine1 to engineCopy(which is also referencing the same memory location), === returns true.

let engine1 = Engine(horsepower: 200 )
let engine2 = Engine(horsepower: 200 )
let engineCopy = engine1

engine1 === engineCopy // true
engine2 === engineCopy // false

However, in the second check, we can see that engine2 and engineCopy are pointing to entirely different objects, so even though thehorsepoweris the same,=== returns false.

What is the difference between is, as, as?, and as!?

3 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Typecasting is a method of changing an entity from one data type to another.

These keywords are used to support typecasting in Swift and allow us to check the type of an instance or to treat an instance as one of the classes in its class hierarchy. This definition will hopefully make much more sense when we look at some examples.

It may be useful to understand the following terminology:

Upcasting: You cast an instance from a subclass toa superclass Downcasting: You cast an instance from a superclassto a subclass

Casting doesn鈥檛 actually modify the instance or change its values. The underlying instance remains the same; it鈥檚 simply treated and accessed as an instance of the type to which it has been cast.

Here鈥檚 the class hierarchy we鈥檒l use in the following examples:

protocol Animal {}
class Mammal: Animal {}

class Dog: Mammal {}
class Cat: Mammal {}

is (typecheck operator) Use the typecheck operator, is, to check whether an instance is of a certain subclass type:

let dog = Dog()

// Output: true
print(dog is Animal)

// Output: true
print(dog is Mammal)

// Output: false
print(dog is Cat)

It鈥檚 important to recognize that this keyword only returns a boolean. It does not perform any conversion - it simply checks to see if a potential type conversion could occur.

This keyword isn鈥檛 very popular in Swift as you can always write an equivalent expression using an if let and as? instead which would have the added benefit of actually performing the conversion for you.

We鈥檒l see an example of this shortly.

as (upcasting operator) The as operator allows us to upcast from a subclassto superclass (i.e. Dog to Animal).

The compiler must be able to guarantee the validity of the cast when we use this operator.

So, we鈥檒l typically use it for conversions we know the compiler will be able to verify like String to NSString, NSDate to Date, or casting an objectback to its parent class type.

let animal: [Animal] = [Dog() as Animal, Cat() as Animal, Mammal() as Animal]

// Output: [ExampleApp.Dog, ExampleApp.Cat, ExampleApp.Mammal]
print(animal)

as? (conditional cast operator) Similar to the as operator, as? also attempts to converta class鈥檚 type, but will return nil if the conversion fails.

Use the as? operator when you aren鈥檛 sure if the casting operation will succeed. In the example below, the attempt to downcast mammal to Dog succeeds,but attempting to cast mammal to Cat evaluates to nil.

let mammal: Mammal = Dog()

if let dog = mammal as? Dog {
// Valid
print(dog)
}

// This expression will evaluate to nil
if let cat = mammal as? Cat {
print(cat)
} else {
print("Downcasting failed!")
}

as! (forced casting keyword) This operator is known as the force downcasting operator and, like all other force unwrapping, will trigger a runtime error if the downcast conversion fails.

Make sure you only use this when you know the downcast will succeed!

let mammal: Mammal = Dog()

// Downcast succeeds
let dog = mammal as! Dog

// Triggers runtime error
let cat = mammal as! Cat

What is the difference between try, try!, and try??

2 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

All of these options are different ways of calling a function that can throw an error.

try! is the most dangerous option of the bunch andshould seldomly be used as it involves force unwrapping an Optional value.

It鈥檚 effectively saying that while the function in question could throw an error, this will never happen. So, we want to proceed as if the called function will always return a value:

func fetchData() {
// try! states that even though this function may return an error,
// we know it won't happen so we can force unwrap this optional
let unwrappedData =try! thisFunctionCanThrow()

// The application will crash if unwrappedData is in fact nil
}

func thisFunctionCanThrow() throws -> [String] {
// Imagine this function can throw an error
return []
}

However, try! is sometimes used if the error is so significant and unrecoverable that no valid user flow exists - similar to the use case of fatalError().

try? can be used to ignore any errors from the throwing function. If an error is thrown, the expression will resolve to nil. As a result, we鈥檒l need to unwrap the returned value in order to use it.

This variation is often used when the error thrown isn鈥檛 important enough to block or change the user鈥檚 experience or it鈥檚 permissible to have the function call fail silently:

func fetchData() {
// The result from `thisFunctionCanThrow` will either be nil
// or a [String] which we'll need to unwrap to access
if let unwrappedData = try? thisFunctionCanThrow() {
print("Successfully retrieved data: \(unwrappedData)")
}
}

func thisFunctionCanThrow() throws -> [String] {
// Imagine this function can throw an error
return []
}

try is the safest option and forces us to explicitly catch and handle any issues that occur:

func fetchData() {
// This is a safe approach and allows us to decide how we
// handle the error
do {
let data = try thisFunctionCanThrow()
print("Successfully retrieved data: \(data)")
} catch {
print("An error occurred:\(error)")
}
}

func thisFunctionCanThrow() throws -> [String] {
// Imagine this function can throw an error
return []
}

What is type inference?

One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Type inference is the mechanism that enables Swift to infer the type of a variable without us having to specify it explicitly. This generally lends itself to writing cleaner and more concise code without compromising readability or type safety.

That鈥檚 why we can write this:

var welcomeMessage = "Hello"

Instead of having to write (a.k.a type annotations):

var welcomeMessage: String = "Hello"

The compiler is able to infer that welcomeMessage is a String based off of the default value we鈥檝e provided.

If we don鈥檛 specify a default value, then we鈥檒l need to use type annotation to provide the compiler with the relevant information:

var red, green, blue: Double

What's the difference between Self vs self?

One min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: 馃敆 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

In the context of protocols and extensions, Self refers to the type that conforms to the protocol whereas self refers to the value inside that type.

Consider this extension:

extension Int {
// Self here refers to the conforming type (Int)
func square() -> Self {
// self refers to the value of the Int itself i.e. 2
self * self
}
}

// self in the code above would now equal 2
let width = 2

// Output: 4
print(width.square())

Additionally, you can use Self to limit the conformance of a protocol to only specific types:

protocol Squareable where Self: Numeric {
func square() -> Self
}

extension Squareable {
func square() -> Self {
self * self
}
}

Now, our Squareable protocol is only available to types that conform to Numeric.