Skip to main content

44 questions tagged with "Swift"

Swift tag description

View All Tags

While iterating through an array, how can we get both the value and the index?

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

Main Source: 🔗 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

We can accomplish this easily with the help of the enumerated()function.

let title = ["Ace", "The", "iOS", "Interview"]

for (index, value) in title.enumerated() {
print("Index: \(index), Value: \(value)")
}

// Index: 0, Value: Ace
// Index: 1, Value: The
// Index: 2, Value: iOS
// Index: 3, Value: Interview

The enumerated() function returns a sequence of pairs composed of the index and the value of each item in the array. Then, we can use tuple destructuring and aforloop to go through every element in the sequence.

A common mistake is to apply this function on a Dictionary.If you do this, the Dictionary will be treated as an array of tuples and your output will look like this:

let userInfo: [String: Any] = ["age": 25 , "gender": "male"]
for (index, value) in userInfo.enumerated() {
print("Index: \(index), Value: \(value)")
}

// Index: 0, Value: (key: "age", value: 25)
// Index: 1, Value: (key: "gender", value: "male")

Your app is crashing in production. What do you do?

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

Main Source: 🔗 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Unfortunately, there’s no one size fits all answer here, but the following explanation might provide a starting point.

The main focus of your answer should be on discussing an approach for isolating the bug, understanding how to replicate it consistently, how you would go about resolving the issue, creating a new release, and finally what preventative measures you would introduce as a result.

You can start off your answer by mentioning that you’ll try and identify the iOS version, app version, and device type of the affected users. Then, if you have access to the device logs, you’ll use them to help you reproduce the crash consistently. Knowing the exact steps that precipitated the crash will allow us to write better tests.

You could also mention that as part of the exploration into the issues, you’d use Exception Breakpoints, Debugger output, crash logs, etc. to help isolate the crash.

Or, if the issue is harder to replicate, you can mention that you might use the Debugger to manually set the application into all of its potential states. For example, if there was a crash during the sign in flow, you might manually put your app into the following states for testing purposes: logged in user, blocked user, account recovery mode, etc.

Subsequently, you might mention that once you’ve identified the offending area of code, you’ll look at recent pull requests that introduced changes in that area and work backwards from there.

You want to demonstrate that you’ll be systematic and methodical in your debugging approach.

Eventually, once you’ve identified a fix, you could talk about how you’ll test the fix thoroughly and ensure that it works across different device types and app configurations (e.g. user account types, languages, regions, etc.).

It may also be prudent to discuss that you’d write additional tests to cover this area of code to ensure the issue doesn’t happen again and to protect against future regressions.

With the fix in place, you can notify your team about the new hotfix and request a code review. You could also mention that if this crash is critical, you would request an expedited review from Apple.

Once the fix has been released, you can discuss how you’ll monitor the new release and ensure that the reported crash numbers do in fact reduce and that your fix was successful.

With the crisis averted, your answer can now focus on how you’d prevent issues like this in the future.

You could bring up leveraging App Store Connect’s phased release feature which will slowly release the new version of your application. This will allow you to prevent untested code from becoming immediately available to all users. With a phased release, if you detect stability issues, you can easily pull the release before it affects a larger percentage of your user base.

Your final point could be about starting a discussion with your team to better understand how this issue made it past your tests and the code review process.

The main objective here is to demonstrate a methodical approach that covers identifying the issue, resolving it, and how you would prevent similar issues moving forward.

What are trailing closures?

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

Main Source: 🔗 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Trailing closures are simply syntactic sugar in Swift that allow us to implement closures without a ton of boilerplate code. When the final parameter in a call to a function is a closure, trailing closures allow you to define the closure’s contents outside of the function call.

func sayHello(name: String, closure: (() -> ())) -> Void {
print("Hello \(name)")
closure()
}

// Typical syntax
sayHello(name: "Aryaman", closure: {
print("Finished saying hello.")
})

// With trailing closure syntax
sayHello(name: "Aryaman") {
print("Finished saying hello.")
}

While this is convenient, convention is to only use trailing closure syntax when your function only has one closure parameter. If your function has multiple closure parameters, you should adopt the normal syntax instead.

func sayHello(name: String, then: (() -> ()), finally: (() -> ())) -> Void { print("Hello \(name)")
then()
finally()
}

// With trailing closure syntax
sayHello(name: "Aryaman") {
print("This is the then closure")
} finally: {
print("This is the final closure")
}

// Preferred approach
sayHello(name: "Aryaman", then: {
print("This is the then closure")
}, finally: {
print("This is the final closure")
})

You can see in the example above, the first call to sayHello() is harder to read than the second call. When multiple closures are required, it’s hard to discern which one does what. I’d recommend you keep this in mind when completing your take-home assignments. Additionally, if development team are using a linter like SwiftLint, it will also enforce the same convention:

  • Use trailing closure syntax when there is only one closure
  • Use the long form syntax when there are multiple closures

What is the difference between an escaping closure and a non-escaping closure?

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

Main Source: 🔗 Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

If you’ve ever tried to pass in a closure as a parameter to a function, you might have encountered a warning from Xcode prompting you to add the @escaping keyword to your parameter declaration. Let’s take a closer look at the difference between an escaping and non-escaping closure. In simple terms, if the closure will be called after the function returns, we’ll need to add the @escaping keyword. Since the closure is called at a later date the system will have to store it in memory. So, because the closure is a reference type, this will create a strong reference to all objects referenced in its body. So, @escaping is used to indicate to callers that this function can potentially introduce a retain cycle and that they’ll need to manage this potential risk accordingly.

escapingClosure {
print("I'll execute 3 seconds after the function returns.")
}

nonEscapingClosure {
print("This will be executed immediately.")
}

func escapingClosure(closure: @escaping (() -> ())) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
closure()
}
}

func nonEscapingClosure(closure: (() -> ())) {
// Some synchronous code…
// Calls the closure immediately
closure()
// Some synchronous code…
}

In the case of the nonEscapingClosure, we can see that the closure is called immediately and will not live or exist outside the scope of our function. So, the non-escaping closure variety works for us here. This is also the default type for all closures in Swift. As an added benet, we can use the self keyword here without worrying about introducing a retain cycle.