Skip to main content

38 questions tagged with "General"

General tag description

View All Tags

What are Swift’s different assertion options?

· 4 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Assertions allow us to terminate the execution of our program if a condition is not met at runtime.

While this may seem like a drastic reaction, this feature makes the debugging process far easier. By terminating the execution of our app in this way, we give ourselves an opportunity to investigate why our assertion failed and to make whateverchanges are necessary.

Swift provides three different assertion types:

assert()

This is useful for basic debugging and allows you to assert that a condition is true before the execution of your program can continue. In the event that the condition is not satisfied, the system will terminate your application, but will allow it to remain in a debuggable state.

assert() is only evaluated in Debug mode and is disabledin all Release builds.

This enables you to easily sanity check your implementation during development while ensuring that your end user’s experience is unaffected.

assert(age > 0 && age < 150 , "Either an invalid age or a medical marvel.")
assert(user.isAuthenticated, "User should be authenticated at this stage.")

guard let storageKey = model.key as? String else {
// assertionFailure() allows us to trigger the failure case directly
// without having to evaluate a conditional expression
assertionFailure("Cannot save model without a storagekey")
return
}

It’s important to note, though, that your project’s optimization settings can change the behaviorof this function:

  • In Playground and -Onone builds (the default for Xcode’sDebug configuration), if the condition evaluates to false, the program will print the specified message and stop execution, but will remain in a debuggable state.
  • In -O builds (the default for Xcode’s Release configuration),the assert() is not evaluated.
  • In -Ounchecked builds, the assert() is not evaluated, but the optimizer may assume that it always evaluates to true which can easily mask serious programming errors.

precondition()

You can think of precondition() as assert() with support for Release builds. This assertion type will also stop your program’s execution when a condition is not met.

To clarify the difference between assert() and precondition() further, assert() is generally used for sanity checking your code during development whereas precondition() is used for guarding against situations that, if they happen, mean your program cannot reasonably proceed. For example, you might use precondition() to validate that the arguments passed into your function match the requirements specified by your documentation.

precondition(state != .invalid,"App in unknown and non-recoverable state.")
precondition(containers.count > 0 , "Empty containerstack.")
preconditionFailure("Cannot cast\(type(of: objCValue)) to \(Value.self)")
preconditionFailure("Plist file not found")

Similar to assert(), the particular compiler optimizationsused in your project can influence the function’s behavior:

  • In Playground and -Onone builds (the default for Xcode’sDebug configuration), if the precondition()evaluates to false, the program willprint a message and stop execution, but will remain in a debuggable state.
  • In -O builds (the default for Xcode’s Release configuration),if theprecondition() evaluates to false, the program’s execution is stopped.
  • In -Ounchecked builds, precondition() is not evaluated,but the optimizer may assume that it always evaluates to true which can easily mask serious programming errors.

fatalError()

This assertion type is used in situations where the application has encountered a significant enough error that there is no reasonable way to proceed. If you use fatalError() to handle such a scenario, the application will immediately terminate in both Debug and Release builds.

fatalError("Unable to load image asset named \(name).")
fatalError("init() has not been implemented")
fatalError("ViewController is not of the expected class \(T.self).")

The use of this assertion type can be a little polarizing. You’ll find that some developers are vehemently against using it while others see certain advantages and respect the deliberateness it offers. Regardless of which camp you find yourself in, you’ll want to use this sparingly and only when your application enters a truly unexpected and unrecoverable state.

Unlike the other options we’ve discussed so far, fatalError() will work regardless of any compilation or optimization settings you’ve enabled on your project.

By using these different types of assertion mechanisms, we can articulate the assumptions in our code and, as a result, write code that is extremely clear about the behavior we expect.

What are the differences between a class and a struct?

· 3 min read
Ace the iOS Interview
Aryaman Sharda
Sources & Resources
FeaturesClassesStructs
TypeReferenceValue
Implement ProtocolsYesYes
Define PropertiesYesYes
Define MethodsYesYes
Define InitializersYesYes
Be ExtendedYesYes
Support InheritanceYesNo
TL/DR

The main takeaway here is that a class is a reference-type and a struct is a value-type.

When you pass reference-types around in your program (i.e. as a parameter), you are actually passing a direct reference to the memory location of that object. As a result, different parts of your program can easily share and modify your object since they’re all referencing the exact same place in memory.

When you pass a struct or enum to a function, you’re only passing along a copy of the data in them. So, even if you modify properties of the passed in value-type, the original one remains unchanged as you’re effectively just modifying a duplicate. This type of behavior is called pass-by-value semantics and is helpful in preventing situations where one part of the code inadvertently changes values or unknowingly affects the application’s state.

Both a class and a struct can implement protocols, define properties, methods, initializers, and be extended, but only a class can support inheritance and by extension polymorphism.

Since a struct requires less overhead to create and is safer to use due to it’s immutability and pass-by-value semantics, Apple’s recommendation is to start with a struct when you can and change to aclassonly when needed.

However, if the entity you’re working with contains a lot of data, then it’s probably useful for it to be a class so you can share a reference to it and only incur the memory cost once.

In Bullets

Structs​

  • Value Type: Copied when assigned or passed around.
  • Immutability: Instances are immutable by default (unless marked as var).
  • Memory Management: Managed on the stack for performance.
  • Inheritance: Cannot inherit from other structs.
  • Initialization: Automatically gets a memberwise initializer.
  • Use Case: Ideal for small, lightweight data structures like points, sizes, or ranges.

Classes​

  • Reference Type: Referenced when assigned or passed around; changes affect all references.
  • Mutability: Can change its state and properties if it’s a var reference.
  • Memory Management: Managed on the heap, with automatic reference counting (ARC).
  • Inheritance: Can inherit from other classes, supporting polymorphism.
  • Initialization: Requires explicit initializers and has default initializer if not provided.
  • Use Case: Suitable for more complex data structures, especially when you need inheritance or shared mutable state.

What are the differences between a delegate and a notification?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Delegates and notifications are different mechanisms for informing objects about events or actions taking place in other parts of your app. Though they differ fundamentally in how they communicate, both of these options can help reduce coupling between entities in your code.

Let's look at delegates first.

A delegate is like a phone call - not only can you communicate with the person on the other end, but they can also communicate with you. In a similar way, delegates are used to establish two-way communication between objects.

There's no need for the delegating object to know specifics about the object it's talking to; the receiving object simply needs to implement the required protocol.

Typically, delegates are used when you want the object receiving the events to influence the sending object. We know, for example, that a class that implements the UITableViewControllerDelegate will be notified whenevents occur in a UITableView (e.g. the selection of a cell). This class can then handle the event in whatever way it wishes (e.g. present a new view, issue a command toUITableView,etc.).

In contrast, a notification is more of a broadcast than a strict two-way communication. For example, whenever a user changes time zones or night mode is activated, iOS notifies all listening objects so they can adjust accordingly.

Notifications help reduce coupling between the sending and receiving objects as the sending object simply publishes a notification, but is unaware of who or if anyone is listening. Unlike with delegates, the objects receiving the notification cannot communicate with or interact with the sender as notifications are only a one-way communication.

In summary, the main difference between a delegate and a notification is that the former is used for one-to-one messaging while the latter is used for one-to-many messaging.

The appropriate choice will depend on your use case.

What are the differences between a library and a framework?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

The difference between a library and a framework is that you’ll typically call a library, but a framework will call you.

Library

A library is a collection of functions that each perform some work and then return control to the caller. Libraries can only contain executable code; no other assets or media.

Libraries usually contain highly tested and sophisticated code meant to address some particular problem. So, it’ll often make sense to reuse code written by other developers rather than implementing it from scratch yourself. For example, many libraries exist for complicated topics like physics, audio processing, image manipulation, etc. Clearly, it would be unrealistic to implement all of that yourself.

You can think of a library like a trip to IKEA. You already have a home filled with furniture, but there's a few rooms that you need help furnishing. Instead of making it yourself, you can go to IKEA (the library) and pick and choose the relevant pieces you need. Throughout this experience, you - the programmer - are in control.

An example on iOS would be the open-source Charts library which lets you easily create bar, line, and pie graphs. It provides all of the functionality, but it's up to the programmer to decide exactly when, where, and how it should be used.

Framework

A framework is similar to a library, but instead leaves openings for you to influence its behavior and execution. This is accomplished most commonly through subclassing, dependency injection, and delegation. Unlike libraries, frameworks can contain other media types (images, audio, etc.) - not just code.

The main distinction between a library and a framework is that there is an inversion of control. When you use a framework, you'll have a few opportunities to plug in your code, but the framework is in charge of when - and if at all - your custom code is executed. The framework has effectively inverted the control of the program; it's telling the developer what it needs and decides when to execute it.

Consider a framework like Vapor or a location tracking framework. They both provide complicated functionality, but leave little openings for you to introduce your own custom logic.

What are the differences between Keychain and UserDefaults?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

UserDefaults and Keychain are both useful in storing small key-value pairs on the user's device, but their security capabilities differ greatly.

Keychain is the only native option for storing data on an iOS device in an encrypted manner.

It's typically meant for storing small amounts of data like an access token, credentials, or other sensitive information. However, while it is still the most secure offering on iOS, it’s important to know that Keychain data can be accessed on jailbroken devices.

Since Keychain is implemented as an SQLite database stored on the file system, it is slower than UserDefaults. Lastly, values stored in the Keychain will persist across application deletions and re-installs unless explicitly deleted.

UserDefaults also allows you to store key-value pairs across different invocations of your app, but it is not secure.

Values stored in UserDefaults are eventually written to a .plist file which are entirely human-readable.UserDefaults are usually used to store basic key-value pairs and user preferences. If you don't need the Keychain's security features, UserDefaults are the more convenient choice. Finally, unlike in the Keychain,items saved in UserDefaults will not persist across application deletions and re-installs.

What are the differences between the static and class keywords?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Both static and class keywords enable us to attach methods directly to a type rather than to an instance of the type. However, they differ in their ability to support inheritance.

When we use the static keyword on a function declaration,that function can no longer be overridden by a subclass. However, if we were to use the class keyword instead, overriding this function in a subclass would still be a possibility.

As we’ve previously discussed, thefinalkeyword attachedto aclassor function also prevents it from being subclassed or overridden. Therefore, it may be easier to remember that staticis equivalent tofinalclass.

class Dog {
class func bark() -> String {
return "Woof"
}

static func sit() -> Void {}
}

class ScoobyDoo: Dog {
override class func bark() -> String {
"Zoinks!"
}

// ERROR: Cannot override static method
override static func sit() -> Void {}
}

As a final point, since functions declared with the class keyword can be overridden, this means that they must be dynamically dispatched. In contrast, and unsurprisingly, when you declare a function with thestatickeyword, that functioncall is now statically dispatched.

What are the different execution states your application can be in?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

There are 5 distinct states an iOS app can find itself in:

Not running This is when the app has not been launched or was previously running but has now been terminated by the system.

Inactive

This occurs when the app is actively running in the foreground, but is not receiving events. This state tends to be brief as the app transitions to some other state.

Active

This is the normal mode for most apps; the app is running in the foreground and receiving and responding to events.

Background

This state describes an app that is running in the background, but is still executing code. Most applications tend to enter this state on the way to being suspended. Furthermore, apps that require extra execution time may remain in this stage longer.

Apps that are launched directly into the background will enter this state instead of the inactive state.

Suspended

This state describes an application that is running in the background but is not executing code.

Apps that are suspended will remain in memory, but will be terminated by the system if a low-memory condition occurs.

What are the different URLSessionConfiguration options?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

URLSession is the main object responsible for managing HTTP & HTTPS requests in iOS.

In order to create a URLSession, we need to initialize it with a URLSessionConfiguration type:

URLSession(configuration: .default)

The default URLSessionConfiguration uses a persistent disk-based cache and stores credentials in the user’s keychain.

Next, we have the ephemeral URLSessionConfiguration type.

URLSession(configuration: .ephemeral)

It’s similar to the default configuration, however it doesn’t maintain a cache, store credentials, or save any session-related data to disk. Hence, the name “ephemeral”.

Instead, all session-related data is stored in RAM. The only time an ephemeral session writes data to disk is when you explicitly instruct it to write the contents of a URL to a file.

Finally, we have the background configuration type:

URLSession(configuration: .background(withIdentifier: "IDENTIFIER_NAME"))

This creates a URLSessionConfiguration object that allows HTTP and HTTPS uploads or downloads to be performed in the background. This configuration is most commonly used when transferring data files while the app runs in the background.

A URLSession configured with this type hands controlof the transfer over to the system which then handles the file transfer in a separate process. In iOS, this configuration makes it possible for transfers to continue even when the app is suspended or terminated.

What are the stages in a UIViewController’s view lifecycle?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Here are the different stages of a UIViewController’s lifecycle:

loadView() This function is responsible for creating the view the UIViewController manages. If you want to create your view manually, you’ll need to override this function.

However, if you’re working with .storyboard or .xib files, you can ignore this method entirely.

loadViewIfNeeded() Loads the UIViewController’s view if it has not yet been loaded.

viewDidLoad() This method is called after the UIViewController has loaded its view hierarchy into memory.

This method is called regardless of whether the view hierarchy was loaded from a .xib file, .storyboard, or created programmatically in the loadView() method. In most cases, you’ll override this method in order to perform additional setup and customization.

viewWillAppear(_ animated: Bool) This method is called before the UIViewController's view is about to be added to a view hierarchy and before any animations are configured for showing the view. You can override this method to perform custom tasks associated with displaying the view. For example, you might use this method to change the orientation or style of the status bar to coordinate with the orientation or style of the view being presented.

According to Apple’s documentation, this method “notifies the UIViewController that its view is about to be added to a view hierarchy”, however this method gets called every time the view is about to appear regardless of whether the view has previously been added to the hierarchy.

viewWillLayoutSubviews() This method is called to notify the UIViewController that its view is about to layout its subviews. In other words, this method is called right before layoutSubviews() is executed.

For example, when a view's bounds change, this function will be called as the view needs to adjust the position of its subviews and the layout will need to be recalculated. Your view controller can override this method to make changes before the view lays out its subviews. The default implementation of this method does nothing.

viewDidLayoutSubviews() As you’d expect, this method is called to notify the view controller that layoutSubviews() has finished execution.

This method being called does not indicate that the individual layouts of the view's subviews have been adjusted as each subview is responsible for adjusting its own layout.

YourUIViewControllercan override this method tomake changes after the view lays out its subviews. The default implementation of this method does nothing.

viewDidAppear(_ animated: Bool) This function notifies the UIViewController that its view has been added to the view hierarchy and is now visible on the screen.

You can override this method to perform additional tasks associated with presenting the view. If you override this method, you must callsuperatsome point in your implementation.

viewWillDisappear(_ animated: Bool) This method is called when the UIViewController is going to be removed from the view hierarchy or will no longer be visible. This method is called before the view is actually removed and before any animations are configured.

viewDidDisappear(_ animated: Bool) This method is called when the UIViewController is removed from the view hierarchy.

What common problem does the Main Thread Checker help detect?

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

Main Source: đź”— Ace the iOS Interview

Additional Sources:

Further Reading:

TL/DR

Quite simply, it’s a means of identifying code that should be running on the main thread but is running on a background thread instead.

Since all UI updates should only be performed on the main thread, the Main Thread Checker is often used to help catch instances of UI updates occurring on a background thread.

To enable it, select Edit Scheme → Test → Diagnostics and toggle on the Main Thread Checker:

With this setting enabled, we’ll get the following warning if we try and update the UI on a background thread: