All topics

QUESTIONS AND ANSWERS

iOS

How do Swift’s type system and optionals improve code safety?

Swift checks types at compile time and explicitly represents the absence of a value with Optional, written using ?. Safely access an optional with optional binding or chaining; force-unwrapping it with ! can cause a runtime error if it contains nil. This makes absence part of an API’s design instead of leaving it implicit. Types and optionals in The Swift Programming Language

What is the difference between value types and reference types in Swift?

Structures and enumerations are value types: assigning or passing them to a function gives them independent value semantics. Classes are reference types: multiple variables can point to the same mutable instance. Choose a struct when a model represents a value and shared state should be avoided; choose a class when you need shared identity, inheritance, or reference semantics. Array, Dictionary, and String are value types too. Value and Reference Types in Swift

What are protocols and generics useful for in an iOS architecture?

A protocol defines capabilities that different types can implement, while generics let you write functions or types that work with any type meeting specified constraints. Together, they support compiler-checked boundaries, such as an abstract repository with real and test implementations. You don’t need a protocol for every type: use one when it enables substitution or expresses a useful abstraction. Protocols and Generics

How does ARC manage memory, and how can you break a retain cycle?

Automatic Reference Counting (ARC) releases a class instance when no strong references to it remain. A cycle happens when two objects strongly retain each other — for example, a delegate and its owner — so they stay alive even after the screen no longer needs them. Inspect references in the memory graph and use weak or unowned only when the ownership relationship calls for it; a weak reference becomes nil when its object is deallocated. Automatic Reference Counting and diagnosing memory issues in Xcode

How is a SwiftUI app structured, and where is navigation modeled?

App is the entry point, marked with @main; its body declares one or more scenes, and each scene contains a hierarchy of views. WindowGroup usually represents the main window. For hierarchical navigation, NavigationStack maintains a path of Hashable values that SwiftUI maps to screens through navigationDestination. This lets you represent and restore navigation as state without coupling it to a manually created sequence of views. App and NavigationStack

What does the scene-based lifecycle mean, and how should an app respond when it moves to the background?

SwiftUI manages each scene’s lifecycle. ScenePhase indicates whether a scene is active, inactive, or in the background; read from a view, it describes that view’s scene, while read from App, it summarizes the app’s scenes. You can react to pause work or save state, but don’t depend on a final callback before the system suspends or terminates the app: persist important changes while it’s in use. Scenes and ScenePhase

What does it mean that SwiftUI is declarative, and what role does view identity play?

In SwiftUI, you describe the interface that corresponds to the current state, and the framework calculates the necessary updates; you don’t manually issue every change to the view tree. Identity lets SwiftUI associate a view with its state across updates. In lists, use stable, unique identifiers; changing an identity can make the framework treat a view as new and reset its local state. SwiftUI performance

How does SwiftUI decide which views to update when data changes?

Keep one source of truth in the nearest common ancestor. @State stores local, transient state; ordinary properties provide read-only data, and @Binding lets a child modify state owned by its parent. With Observation, an @Observable model can be shared and views update when the properties they read change. @State follows the view’s lifecycle and isn’t persistent storage. Managing user interface state and Managing model data in your app

Which responsibilities should be separated in an app’s architecture?

Separate presentation, domain rules, and data access as the product requires, so a view doesn’t need to know networking or storage details. SwiftUI doesn’t require a single pattern such as MVVM; a view, an observable model, and small services may be enough. Injecting dependencies — for example, a data service — lets you test rules without relying on the network and avoids adding layers that don’t provide a useful boundary. Managing model data in your app

How do you model programmatic navigation and deep links in SwiftUI?

With value-based navigation, NavigationStack state can be a typed collection or a NavigationPath. When a deep link arrives, validate the URL and convert its components into a valid app route; then rebuild the destination with navigationDestination. Keep path elements lightweight — for example, identifiers — and load models at the destination instead of carrying large objects through navigation. Understanding the navigation stack

How do you design a robust network request with URLSession?

URLSession runs data or download tasks; check both transport errors and the HTTP status code, and validate the response structure when decoding it. Represent loading, success, and error states in the presentation model, and don’t assume a connection or valid response will always be available. Add timeouts, cancellation, and retries only when appropriate for the operation; avoid automatically repeating mutations that aren’t idempotent. URLSession

How can you use async/await without blocking the interface or leaving orphaned work?

async and await express operations that can suspend while waiting, for example, for a network response. Structured concurrency (async let and task groups) keeps child tasks within the scope that created them and propagates cancellation. Cancellation is cooperative: the operation must check for it or call code that respects it. Creating a Task doesn’t by itself move heavy synchronous work to the background. Concurrency in Swift and Task cancellation

What do actors protect, and when should you use MainActor or Sendable?

An actor serializes access to its isolated state to prevent data races; its methods can interleave at await points, so recheck invariants after suspension. @MainActor isolates operations that interact with the interface. Sendable communicates that a value can safely cross concurrency domains. Enabling Swift 6 language mode applies strict concurrency checks, although the diagnostic and isolation level also depend on the project configuration. Swift concurrency and MainActor

How do you choose among UserDefaults, files, SwiftData, and Keychain?

Use UserDefaults for small, non-sensitive preferences; files for documents or data whose format and access you manage; SwiftData for persistent models and queries; and Keychain for credentials, keys, and other secrets. Don’t store passwords in preferences or implement your own cryptography. Treat synchronization, migrations, size, and offline use as separate storage requirements. SwiftData and Keychain Services

How does SwiftData connect models, queries, and persistent storage?

@Model describes persistent classes; ModelContainer configures the schema and storage, while ModelContext inserts, fetches, deletes, and saves instances. In SwiftUI, attach a container to the view tree so @Query and the environment context can access it. For durable data, plan schema changes and migrations; for tests, an in-memory configuration avoids changing real storage. Preserving your app’s model data across launches

How can you integrate UIKit into a SwiftUI app or migrate an existing app gradually?

SwiftUI can host a UIKit view with UIViewRepresentable and a view controller with UIViewControllerRepresentable; the coordinator connects delegates and callbacks. In a UIKit app adopting SwiftUI, UIHostingController can present SwiftUI views. Keep one source of truth and define clearly which side owns the lifecycle to avoid ambiguous two-way synchronization. UIViewRepresentable and Migrating to the SwiftUI life cycle

How should you organize unit, integration, and UI tests?

Unit tests verify isolated logic; integration tests check that multiple components work together; UI tests verify critical flows through the interface. Swift Testing provides suites, parameterization, and asynchronous test functions; XCTest remains important for UI automation with XCUIAutomation and performance testing. A test bundle can contain both frameworks, but don’t mix their APIs within the same test. Testing in Xcode and XCTest

How do you test asynchronous code and keep tests deterministic?

Test observable outcomes — data, errors, and state changes — without relying on arbitrary delays. Inject a fake service to simulate responses, failures, and cancellation; cover boundary and error paths as well as success. Swift Testing supports asynchronous and parameterized tests; XCTest also provides expectations for coordinating asynchronous work. Keep tests independent of network state and external accounts. Swift Testing and asynchronous tests in XCTest

What makes UI tests with XCUIAutomation reliable?

Automate important user tasks by locating controls through accessibility and checking visible outcomes, not implementation details. Use controlled data and a reproducible starting state; avoid fixed waits when the framework can wait for a condition. Reserve UI tests for end-to-end flows that justify their higher cost compared with unit tests. XCUIAutomation

What workflow should you follow to diagnose a failure in Xcode?

Reproduce the issue, isolate the affected path, and add a breakpoint to inspect variables and step through the code. For difficult failures, symbolic or exception breakpoints can stop execution close to the source; also review logs and the crash report. If behavior depends on timing, log values with breakpoint actions instead of assuming step-by-step debugging reproduces the same execution. Debugging in Xcode and setting breakpoints

How can you tell a memory leak from expected memory use?

Watch whether memory usage keeps increasing without dropping after screens are closed or work finishes. The Memory Graph shows instances and strong references; Allocations helps track allocations during an interaction. A retain cycle can keep objects alive, but a temporary increase during loading or processing doesn’t prove a leak. Confirm the pattern with a repeatable scenario and test on a real device when iOS memory limits matter. Gathering information about memory use

How do you investigate a slow or choppy SwiftUI screen?

Measure before changing the architecture. Instruments’ SwiftUI template shows long body calculations, frequent updates, and UI hitches. Reduce unnecessary state dependencies, avoid expensive work while calculating the view, and move non-UI operations off MainActor. Test on less powerful supported devices too, since the time available for an update depends on the device and display refresh rate. Understanding and improving SwiftUI performance

What practices improve the performance of lists, images, and background work?

Use lazy containers such as LazyVStack or List when the collection size warrants it, and provide stable identifiers to avoid unnecessary reconstruction. Process images at their display size and release references that are no longer needed. Keep networking and expensive computation off the rendering path, and verify results with Instruments on representative devices instead of optimizing by intuition alone. SwiftUI performance and app responsiveness

How should accessibility and localization be included in the design?

Use semantic controls, descriptive labels, and a coherent reading order for VoiceOver; don’t communicate state through color alone. Check Dynamic Type sizes, contrast, and touch target sizes across accessibility settings. Localize text and formats with Xcode resources, and check text expansion and right-to-left languages when the product supports them. Accessibility fundamentals and localization

What basic practices protect an iOS app’s data and network connections?

Apply least privilege: request access when it’s needed and explain why. Store small secrets in Keychain; keep App Transport Security (ATS) enabled to require secure TLS connections, and avoid HTTP exceptions unless essential. Use system APIs for cryptography and secure transport, minimize sensitive data in logs, and review session handling when users sign out. Keychain Services and preventing insecure network connections

What do code signing, entitlements, and provisioning profiles represent?

Code signing identifies who built the app and helps verify it hasn’t been modified; entitlements declare requested capabilities, such as Keychain groups or notifications. A provisioning profile ties the app, certificate, and allowed capabilities to a particular testing or distribution method. Xcode can manage signing automatically; with manual signing, certificates and profiles must remain compatible with the App ID and distribution channel. Distribution signing and provisioning and entitlements

What should you review before building, archiving, and releasing a version?

Set the deployment target, build and run tests with a Release configuration, and check the app on representative devices and OS versions. Create a distribution archive, verify its signature and entitlements, and test the candidate through a beta channel before release. Review metadata, permissions, and crash reports; a Debug build in the simulator doesn’t replace testing the signed archive. Distributing your app for beta testing and releases

In an interview, how would you design an offline search that avoids stale results?

I’d separate the interface and its states from a search service, store a local copy, and define when the server can refresh it. I’d cancel the previous query when the user changes the text and prevent a slow response from overwriting a newer search; a request identifier or structured task can help control that race. I’d test matches, offline behavior, errors, stored data, and cancellation with a fake service, then measure large searches before choosing indexes or caches. Swift concurrency and Swift Testing

Availability: SwiftData was introduced in iOS 17, and Observation also has minimum system requirements; check API availability against the deployment target. Swift Testing was introduced in Xcode 16. Strict concurrency checks depend on the Swift version and language mode configured for the project. SwiftData, Observation, and Swift Testing