← All courses

SwiftUI Interview Questions & Answers

🗓 May 31, 2026 ⏱ 18 min read

This set covers 88 SwiftUI interview questions with point-wise answers, written from a working iOS developer's point of view. Each answer is concise and structured the way you should speak in an interview.

SwiftUI Fundamentals

1. What is SwiftUI?

  • Apple's declarative UI framework for building interfaces across all Apple platforms.
  • You describe what the UI should look like for a given state, not how to mutate it.
  • The framework diffs and updates the view tree automatically.
  • It works alongside UIKit, not as a hard replacement.

2. What is the difference between declarative and imperative UI?

  • Imperative UI (UIKit) mutates view objects step by step.
  • Declarative UI (SwiftUI) describes the UI as a function of state.
  • Declarative code reduces manual synchronisation bugs.
  • When state changes, SwiftUI re-renders the affected views for you.

3. What is a View in SwiftUI?

  • A View is a protocol with a single body computed property.
  • body returns the view's content as other views.
  • Views are lightweight value types, recreated cheaply on each update.
  • They are descriptions of UI, not the actual rendered objects.

4. Why are SwiftUI views structs and not classes?

  • Structs are value types, so they are cheap to create and copy.
  • Immutability makes view diffing predictable and fast.
  • There is no shared mutable state to cause subtle UI bugs.
  • State that must persist is stored separately via property wrappers.

5. What is the body property?

  • It is the required computed property describing a view's content.
  • SwiftUI calls it whenever the view needs to re-render.
  • It must return a single view (often a container holding many).
  • Keep it cheap because it can run frequently.

6. What is the difference between VStack, HStack, and ZStack?

  • VStack arranges children vertically.
  • HStack arranges children horizontally.
  • ZStack overlays children back-to-front.
  • They are the core layout primitives you compose UIs from.

7. What are view modifiers?

  • Methods that return a new, modified copy of a view.
  • Examples: .padding(), .foregroundColor(), .frame().
  • They are chained, and order matters to the result.
  • Each modifier wraps the view in a new layer.

8. Why does modifier order matter?

  • Each modifier acts on the result of the previous one.
  • .padding().background() differs from .background().padding().
  • The first colours outside the padding; the second inside it.
  • Think of modifiers as wrapping the view in successive boxes.

9. What is some View and opaque return types?

  • some View means "a specific concrete type I won't name".
  • It lets body return complex nested types without spelling them out.
  • The compiler knows the exact type; the caller only knows it conforms to View.
  • It keeps APIs clean while preserving type safety.

10. What is ViewBuilder?

  • A result builder that lets you list multiple views inside a closure.
  • It powers the declarative syntax inside stacks and containers.
  • It supports limited control flow like if and switch.
  • It combines child views into a single composite view.

State Management

11. What is @State?

  • A property wrapper for simple, view-local mutable state.
  • SwiftUI owns the storage and re-renders when it changes.
  • Mark it private since it belongs to that view.
  • Use it for value types like toggles, counters, and text.

12. What is @Binding?

  • A two-way reference to state owned by another view.
  • It lets a child read and write the parent's value.
  • Create one with the $ prefix on a @State property.
  • It avoids duplicating state across the hierarchy.

13. What is the difference between @State and @Binding?

  • @State declares and owns the source of truth.
  • @Binding is a reference to state owned elsewhere.
  • Parents hold @State; children receive a @Binding.
  • This keeps a single source of truth shared safely.

14. What is @StateObject?

  • Owns and keeps alive a reference-type observable object.
  • SwiftUI creates it once and persists it across re-renders.
  • Use it where the object is first created.
  • It prevents accidental re-instantiation on each render.

15. What is @ObservedObject?

  • Subscribes to an observable object owned elsewhere.
  • The view re-renders when the object publishes changes.
  • It does not own the object's lifecycle.
  • Recreating the parent can reset an @ObservedObject, unlike @StateObject.

16. What is the difference between @StateObject and @ObservedObject?

  • @StateObject owns and persists the object across view updates.
  • @ObservedObject just observes an externally owned object.
  • Use @StateObject at the creation site, @ObservedObject when passing it in.
  • Misusing @ObservedObject at creation can cause object loss on re-render.

17. What is @EnvironmentObject?

  • Injects a shared observable object into the view hierarchy.
  • Any descendant can read it without manual passing.
  • It avoids prop-drilling through many intermediate views.
  • The object must be supplied with .environmentObject() up the tree.

18. What is the @Environment property wrapper?

  • Reads system-provided values from the environment.
  • Examples: \.colorScheme, \.dismiss, \.locale.
  • Values propagate down the view tree automatically.
  • You can also define custom environment keys.

19. What is the ObservableObject protocol?

  • A protocol for reference types that publish changes.
  • Mark properties with @Published to trigger updates.
  • Views observing it re-render when those properties change.
  • It is the classic backbone of a SwiftUI view model.

20. What is @Published?

  • A property wrapper that emits a change before a property updates.
  • It works inside an ObservableObject.
  • Subscribed views refresh automatically.
  • It is built on Combine's publisher mechanism.

21. What is the new @Observable macro?

  • A macro (iOS 17+) replacing ObservableObject + @Published.
  • It tracks property access so only views using a property re-render.
  • It reduces boilerplate and improves performance.
  • Pair it with @State or @Bindable in views.

22. What is the "single source of truth" principle?

  • Each piece of state should live in exactly one place.
  • Other views reference it via bindings, not copies.
  • It prevents inconsistent, conflicting UI state.
  • It is central to predictable SwiftUI data flow.

23. What is @AppStorage?

  • A wrapper that reads and writes UserDefaults values.
  • Changes automatically update views and persist across launches.
  • Great for simple settings like theme or onboarding flags.
  • Do not use it for large or sensitive data.

24. What is @SceneStorage?

  • Persists small UI state tied to a scene for restoration.
  • Useful for restoring tab selection or scroll position.
  • The system manages saving and restoring it.
  • It is per-scene, unlike app-wide @AppStorage.

25. When does a SwiftUI view re-render?

  • When state it depends on (@State, observed objects, bindings) changes.
  • SwiftUI recomputes body and diffs the result.
  • Only the parts that actually changed are redrawn.
  • Unrelated state changes should not trigger a redraw.

Layout & Lists

26. How does SwiftUI's layout system work?

  • A parent proposes a size; the child chooses its own size; the parent positions it.
  • This negotiation flows down and back up the tree.
  • Layout is reactive and adapts to content and space.
  • Understanding the proposal cycle explains most layout surprises.

27. What is a GeometryReader?

  • A container that exposes its proposed size and coordinate space.
  • Use it to build size- or position-dependent layouts.
  • It greedily takes all offered space, which can surprise layouts.
  • Use sparingly; prefer simpler layout tools when possible.

28. What is the difference between frame and fixedSize?

  • .frame() proposes a specific size to a view.
  • .fixedSize() lets a view size to its ideal content, ignoring compression.
  • fixedSize prevents text truncation by allowing full intrinsic size.
  • Combine them to control sizing precisely.

29. What is the difference between Spacer and padding?

  • Spacer expands to push siblings apart within a stack.
  • .padding() adds fixed space around a single view.
  • Spacer is flexible; padding is a defined amount.
  • Use spacers for distribution, padding for breathing room.

30. How do you build a list in SwiftUI?

  • Use List with static rows or a data collection.
  • For dynamic data, provide an id or use Identifiable.
  • It handles scrolling, separators, and reuse automatically.
  • It is the SwiftUI counterpart to UITableView.

31. What is the Identifiable protocol and why does List need it?

  • It requires a stable id property uniquely identifying each item.
  • SwiftUI uses the id to track, diff, and animate rows.
  • Without stable ids, rows can glitch or animate wrongly.
  • You can also pass a key path via id: instead.

32. What is the difference between ForEach and a for loop?

  • ForEach is a view that generates child views from data.
  • A normal for loop runs imperatively and cannot return views directly.
  • ForEach needs identifiable data for diffing.
  • It is used inside stacks, lists, and grids.

33. What is LazyVStack versus VStack?

  • VStack creates all children immediately.
  • LazyVStack creates children only as they scroll into view.
  • Lazy variants save memory and improve performance for long content.
  • Use lazy stacks inside a ScrollView for large datasets.

34. How do you create a grid in SwiftUI?

  • Use LazyVGrid/LazyHGrid with GridItem column definitions.
  • GridItem can be fixed, flexible, or adaptive.
  • For simpler aligned layouts, Grid (iOS 16+) is available.
  • Lazy grids render cells on demand for performance.

35. How do you add swipe actions or pull-to-refresh to a List?

  • Use .swipeActions on rows for leading/trailing buttons.
  • Use .refreshable on the list for pull-to-refresh.
  • .refreshable works naturally with async/await.
  • These modifiers replace manual UIKit gesture wiring.

Navigation & Presentation

36. How does navigation work in SwiftUI?

  • Wrap content in a NavigationStack (iOS 16+).
  • Use NavigationLink to push destinations.
  • A bound path array enables programmatic navigation.
  • It replaces UIKit's UINavigationController push/pop.

37. What is the difference between NavigationView and NavigationStack?

  • NavigationView is the older, now-deprecated container.
  • NavigationStack offers a clear, path-driven model.
  • NavigationStack supports type-safe, programmatic navigation.
  • New apps should prefer NavigationStack.

38. What is a NavigationLink?

  • A control that pushes a destination when tapped.
  • It can take a value resolved by navigationDestination.
  • It integrates with the navigation path for programmatic control.
  • Use it inside a navigation container.

39. How do you present a sheet (modal)?

  • Use the .sheet(isPresented:) or .sheet(item:) modifier.
  • Toggle a bound boolean or set an identifiable item to show it.
  • SwiftUI handles the modal presentation and dismissal.
  • Use .fullScreenCover for a full-screen modal.

40. How do you dismiss a view programmatically?

  • Read @Environment(\.dismiss) and call it.
  • For bound presentation, set the boolean back to false.
  • This works for sheets and pushed views alike.
  • It replaces UIKit's manual dismiss/pop calls.

41. What is a TabView?

  • A container presenting tabbed content with a tab bar.
  • Each tab gets a label via .tabItem.
  • It supports a selection binding for programmatic switching.
  • It also powers paged carousels with the page style.

42. How do you show an alert or confirmation dialog?

  • Use the .alert modifier bound to a boolean or item.
  • Use .confirmationDialog for action-sheet style choices.
  • Provide buttons and an optional message inside.
  • SwiftUI manages presentation declaratively from state.

43. How do you pass data during navigation?

  • Pass values into the destination view's initialiser.
  • With value-based navigation, attach a navigationDestination handler.
  • Shared state can flow via environment or a view model.
  • Avoid global singletons for better testability.

44. What is deep linking and how is it handled?

  • Opening a specific screen from a URL or notification.
  • Use .onOpenURL to parse and route the link.
  • Drive a NavigationStack path to reach the target screen.
  • Value-based navigation makes this clean and testable.

45. How do you handle navigation in a large app?

  • Centralise navigation state in a router/coordinator object.
  • Use a bound path array for programmatic control.
  • Represent destinations as an enum for type safety.
  • This keeps deep links and back navigation manageable.

Animations & Gestures

46. How do animations work in SwiftUI?

  • SwiftUI animates the difference between two states automatically.
  • Wrap a state change in withAnimation { } or attach .animation().
  • It interpolates affected properties like position, size, and opacity.
  • You describe the end state; SwiftUI animates the transition.

47. What is the difference between implicit and explicit animation?

  • Implicit uses the .animation(_:value:) modifier tied to a value.
  • Explicit wraps a state change in withAnimation { }.
  • Implicit animates whenever that value changes.
  • Explicit animates only the changes inside the closure.

48. What is a transition in SwiftUI?

  • It defines how a view appears and disappears.
  • Examples: .opacity, .slide, .scale, or custom ones.
  • Applied with .transition() on views added or removed.
  • The insertion/removal must occur inside an animation.

49. What is matchedGeometryEffect?

  • It animates a view smoothly between two positions in a shared namespace.
  • SwiftUI matches the source and destination by id.
  • It powers hero-style transitions between layouts.
  • Both views must use the same @Namespace.

50. How do you handle gestures in SwiftUI?

  • Attach gesture modifiers like .onTapGesture or .gesture().
  • Built-in gestures include tap, drag, long press, and magnify.
  • Combine gestures with simultaneously, sequenced, or exclusively.
  • Gesture state can drive animations and view updates.

51. What is a DragGesture?

  • A gesture that reports translation as the user drags.
  • Use onChanged for live updates and onEnded to finalise.
  • Combine with @GestureState for automatic reset.
  • Common for swipe, slide, and pull interactions.

52. What is @GestureState?

  • A wrapper that tracks transient gesture state.
  • It automatically resets to its initial value when the gesture ends.
  • Ideal for in-progress drag offsets.
  • It avoids manual cleanup after the gesture finishes.

53. How do you create a custom animation curve?

  • Use built-ins like .easeInOut, .spring, or .timingCurve.
  • .spring gives natural, physics-based motion.
  • Adjust duration, damping, and response for the feel you want.
  • Apply the curve through withAnimation or .animation().

Combine, Async & Data

54. How do you load data asynchronously in SwiftUI?

  • Use the .task { } modifier, which runs async work when a view appears.
  • Await network or database calls inside it.
  • It cancels automatically when the view disappears.
  • Store results in observable state to update the UI.

55. What is the difference between .task and .onAppear?

  • .onAppear runs synchronous code when a view appears.
  • .task runs async work and ties it to the view's lifetime.
  • .task auto-cancels when the view goes away.
  • Prefer .task for async loading to avoid manual cancellation.

56. How does Combine integrate with SwiftUI?

  • Publishers feed values into @Published properties.
  • Views observing the object refresh on emissions.
  • onReceive lets a view react to any publisher.
  • Much of this is now replaceable by async/await and @Observable.

57. What is the onReceive modifier?

  • It subscribes a view to a Combine publisher.
  • The closure runs each time the publisher emits.
  • Useful for timers, notifications, or external streams.
  • SwiftUI handles subscription lifecycle for you.

58. How do you debounce user input like a search field?

  • With Combine, apply debounce on the text publisher.
  • With async, use a debounced Task that cancels prior work.
  • It avoids firing a request on every keystroke.
  • This reduces network load and improves responsiveness.

59. How do you bind a text field to state?

  • Pass a @State string via its binding: TextField("", text: $name).
  • Typing updates the state and re-renders dependent views.
  • The binding keeps UI and data in sync two-way.
  • Use @FocusState to manage keyboard focus.

60. What is @FocusState?

  • A wrapper tracking which field currently has keyboard focus.
  • Bind it to fields to read or programmatically set focus.
  • Useful for moving focus between form fields.
  • It replaces UIKit first-responder management.

Interoperability & Architecture

61. How do you use a UIKit view in SwiftUI?

  • Wrap it with UIViewRepresentable.
  • Implement makeUIView and updateUIView.
  • Use a Coordinator to handle delegates and callbacks.
  • This bridges mature UIKit components into SwiftUI.

62. How do you use a UIViewController in SwiftUI?

  • Wrap it with UIViewControllerRepresentable.
  • Implement the make/update controller methods.
  • Use it for things like camera, web views, or existing screens.
  • A coordinator handles delegate callbacks.

63. How do you embed SwiftUI inside a UIKit app?

  • Wrap a SwiftUI view in a UIHostingController.
  • Push or present that controller like any other.
  • It lets you adopt SwiftUI incrementally.
  • State and data can be passed in at creation.

64. What is the Coordinator pattern in representables?

  • A class that bridges UIKit delegate callbacks back to SwiftUI.
  • Created in makeCoordinator().
  • It holds references to bindings and forwards events.
  • It is how imperative UIKit talks to declarative SwiftUI.

65. Which architecture patterns suit SwiftUI?

  • MVVM fits naturally with observable view models.
  • The Composable Architecture (TCA) offers stricter unidirectional flow.
  • Plain MV (model + view) works for simpler apps.
  • Choose based on app size and team preference.

66. Why does MVVM fit SwiftUI well?

  • View models expose observable state the view binds to.
  • Views stay thin and declarative.
  • Business logic is testable outside the UI.
  • It aligns with SwiftUI's reactive data flow.

67. How do you inject dependencies in SwiftUI?

  • Pass them into view-model initialisers.
  • Use the environment for app-wide services.
  • Define custom environment keys for clean access.
  • This enables mocking for previews and tests.

68. How do you share state across the whole app?

  • Create an observable object and inject it with .environmentObject().
  • Descendants read it with @EnvironmentObject.
  • Keep it focused to avoid over-broad re-renders.
  • For iOS 17+, use @Observable with the environment.

Performance, Previews & Testing

69. How do you optimise SwiftUI performance?

  • Keep body cheap and split large views into smaller ones.
  • Use lazy stacks and grids for long content.
  • Scope observable state so only affected views re-render.
  • Avoid heavy work and GeometryReader where unnecessary.

70. Why is splitting large views into smaller ones beneficial?

  • SwiftUI re-renders only views whose state changed.
  • Smaller views localise updates and reduce wasted work.
  • They are easier to read, reuse, and test.
  • It limits how much body recomputes at once.

71. What causes unnecessary re-renders and how do you avoid them?

  • Observing too broad an object refreshes unrelated views.
  • Passing changing closures or values can invalidate views.
  • Use fine-grained observation (@Observable) and stable identities.
  • Split state so changes touch the minimum view set.

72. What are SwiftUI Previews?

  • Live, interactive canvas renders of a view in Xcode.
  • They speed iteration without running the full app.
  • You can preview multiple states, devices, and color schemes.
  • Inject sample data so previews stay isolated.

73. How do you preview multiple states or devices?

  • Return several views from the preview, each with different data.
  • Apply .preferredColorScheme or device traits.
  • Use the #Preview macro (Xcode 15+) for concise setup.
  • This validates light/dark and size variations quickly.

74. How do you make a view accessible in SwiftUI?

  • Add .accessibilityLabel and .accessibilityHint.
  • Group related elements with .accessibilityElement(children:).
  • Support Dynamic Type by using scalable fonts.
  • Test with VoiceOver and large text settings.

75. How does SwiftUI support Dynamic Type?

  • System text styles scale automatically with the user's setting.
  • Use .font(.body) rather than fixed point sizes.
  • Layouts should adapt to larger text without clipping.
  • Test at the largest accessibility sizes.

76. How do you support dark mode?

  • Use semantic colors and system materials that adapt automatically.
  • Read @Environment(\.colorScheme) when you need to branch.
  • Provide asset-catalog colors with light/dark variants.
  • Preview both schemes to verify contrast.

77. How do you localise a SwiftUI app?

  • Use string keys resolved from Localizable.strings.
  • Text automatically looks up localised values.
  • Support right-to-left layouts with leading/trailing alignment.
  • Format numbers and dates with locale-aware formatters.

78. How do you test SwiftUI views?

  • Unit-test view models and logic directly with XCTest.
  • Use UI tests or snapshot tests for view output.
  • Keep logic out of views to maximise testability.
  • Previews also serve as quick visual checks.

Advanced & Cross-Platform

79. How does SwiftUI handle different Apple platforms?

  • The same view code can target iOS, macOS, watchOS, and tvOS.
  • Platform-specific modifiers adapt behaviour where needed.
  • Conditional compilation handles differences.
  • It maximises code sharing across devices.

80. What is the @ViewBuilder attribute used for in custom views?

  • It lets your custom container accept multiple child views.
  • Apply it to a closure parameter to build content.
  • It mirrors how built-in stacks accept children.
  • It enables reusable, composable container components.

81. What is a PreferenceKey?

  • A mechanism to pass data up from child views to ancestors.
  • Children set a preference; an ancestor reads it.
  • Useful for measuring sizes or coordinating layout.
  • It complements the environment, which flows downward.

82. What is the difference between environment and preferences?

  • Environment values flow downward to descendants.
  • Preferences flow upward from descendants to ancestors.
  • Use environment to configure children, preferences to report from them.
  • Together they enable two-way coordination.

83. How do you create a custom ViewModifier?

  • Conform a struct to ViewModifier and implement body(content:).
  • Apply it with .modifier() or a convenience extension.
  • It packages reusable styling and behaviour.
  • It keeps view code DRY and consistent.

84. How do you draw custom shapes and paths?

  • Conform a struct to Shape and implement path(in:).
  • Use Path commands to build lines and curves.
  • Apply fills, strokes, and gradients as modifiers.
  • Shapes animate when their parameters are animatable.

85. What is Canvas in SwiftUI?

  • A view for immediate-mode, high-performance custom drawing.
  • You draw directly in a closure with a graphics context.
  • Good for charts, particles, or many dynamic shapes.
  • It avoids the overhead of many individual views.

86. How do you persist data with SwiftData in SwiftUI?

  • Define models with the @Model macro.
  • Query them in views with @Query.
  • Insert into and save through the model context.
  • It integrates cleanly with SwiftUI's lifecycle.

87. What are common SwiftUI pitfalls to avoid?

  • Using @ObservedObject where @StateObject is needed.
  • Overusing GeometryReader and bloating body.
  • Putting business logic directly in views.
  • Ignoring view identity, causing animation glitches.

88. When should you choose SwiftUI over UIKit?

  • Choose SwiftUI for new apps and rapid, cross-platform UI.
  • Use UIKit when you need mature, fine-grained control or legacy support.
  • Mixed apps are common, bridging both with representables.
  • Consider team familiarity and minimum OS target.